Please send us your annotations! Just email them to 70302.2566@compuserve.com and indicate the topic and the help file that your annotation refers to. Help us build the SDK annotation database! 1/9/97 NOTE: This version fixes a bug that caused some untoward crashes. This version also revises the logic for mstater.exe, and may actually work with Visual C++ 4.2, though I have not tried it yet. 11/21/96 NOTE: The win32.hlp file that ships with Borland C++ v5.01 seems to be missing the help topics that correspond to annotations #126 (AVIFileOpen) and #140 (the STGM structure). 8/29/96 NOTE: Readers have reported that mstater.exe does not work with Visual C++ v4.2. Unfortunately, I don't yet have that version of the compiler. As soon as I get it, I will try to modify mstater.exe to work correctly with it. 5/8/96 NOTE: The AnnTater utility mentioned below has been mutated to work with the InfoViewer that is integrated in the Visual C++ IDE. Instead of running anntater.exe, run mstater.exe if you want to annotate the IDE InfoViewer (rather than any .hlp files you may have). Let me know if you have problems. 12/1/95 NOTE: The WDJ annotation package now includes anntater.exe. AnnTater contains all of our annotations and you can use it to automatically place our annotations in your help file. Although I've tried to test this software in a variety of situations, USE IT AT YOUR OWN RISK. If you have annotations of your own, you may want to back up your .ann file before running AnnTater. To use AnnTater, follow these steps. If you don't have annotations of your own, the easiest route is to delete your .ann file each time you run anntater.exe on a new installment of our annotations. 1) invoke anntater.exe 2) select a type of help file (Windows 3.1 API or Win32 API) 3) if necessary, press the Locate button to specify the path where this helpfile exists 4) select one or more annotations that you want to install 5) press the Copy button 6) press Done Note that the program displays a checkmark beside each annotation that it believes it has successfully installed during the current session. ---------- This is the Windows Developer's Journal SDK Annotation File, a growing collection of useful annotations for the standard online Windows API help file, win31wh.hlp, which is included with every Windows C++ compiler. You can either cut and paste these annotations into your own SDK help file (use ALT-E-A to create a help topic annotation), or you can use anntater.exe to install them automatically for you. If you have a useful annotation you would like to submit to our collection, send it to: 70302.2566@compuserve.com If we use your annotation, you will be listed as the contributor. Our thanks to the dozens of readers whose contributions help us continue to build this database of useful information for the Windows programming community. Windows Developer's Journal "The Magazine for Windows Developers" Windows Developer's Journal is the monthly publication for advanced Windows programmers. We cover a variety of topics such as Windows 95 programming, NT programming device drivers, undocumented functions, compiler bugs, operating system bugs, graphics, MFC, multimedia, communications, and so on -- usually in the form of reusable code that you can use immediately (most of our code has been compiled with both Borland and Microsoft C/C++ compilers). In addition, each month contains regular features like these: * SDK Annotations (they appear in the magazine before they appear in this file) * Bug++ of the Month (nasty bugs in popular C++ compilers) * Tech Tips (user-submitted tips and techniques) * Books in Brief (quick looks at recent books) * Understanding NT (covers NT-specific programming issues) and more. WDJ costs $34.99/year in the US, $45/year Canada/Mexico, or $64/year overseas. To order a subscription, contact Miller Freeman P.O. Box 56565 Boulder, CO 80322-6565 (800) 365-1425 (303) 678-0439 wdsub@rdpub.com To subscribe by email or fax, include your name, address, phone number, MasterCard or Visa number, and expiration date. From CompuServe, that email address would be something like this: >INTERNET:wdsub@rdpub.com Note: Annotation #35 proved to be incorrect and was removed. Note: due to a bug in WinHelp, it is not possible to display or annotate the GetRgnBox topic in the Visual C++ v1.5 version of the helpfile. This prevents #67 from being properly installed. ------------------------------------------------------------ WDJ SDK Annotation #1 TYPE: Win3.1 TOPIC: Dialog Boxes KEYWORD: Dialog Boxes If a listbox is the first control in a dialog's tab order and the dialog box was not created with the WS_VISIBLE style, the listbox does not correctly draw itself with the focus. Reference: p35, March 1993 Windows/DOS Developer's Journal. ------------------------------------------------------------ WDJ SDK Annotation #2 TYPE: Win3.1 TOPIC: Property Lists KEYWORD: Property Lists The oft-quoted maxim that using window properties is slower than using class or window extra bytes (via GetWindowWord()/SetWindowWord()) is totally false if you use a global atom rather than a string to name the property. Properties were about 20% slower than window words under Windows 3.0, but under Windows 3.1 GetProp() is about 300% faster than GetWindowWord(). Reference: p 49, March 1993 Windows/DOS Developer's Journal. ------------------------------------------------------------ WDJ SDK Annotation #3 TYPE: Win3.1 TOPIC: WM_MOUSEMOVE (2.x) KEYWORD: WM_MOUSEMOVE The documentation incorrectly states that the x and y arguments are in screen coordinates. They are in client coordinates. ------------------------------------------------------------ WDJ SDK Annotation #4 (revised) TYPE: Win3.1 TOPIC: SetWindowsHookEx (3.1) KEYWORD: SetWindowsHookEx SetWindowsHookEx() has a bug: using it to install a task-specific hook can cause various failures. The workaround is to pass it a module handle rather than an instance handle. You can obtain a module handle from GetModuleHandle(). If you only have the current instance handle and not the name of the module, use the following undocumented hack under Windows 3.1: GetModuleHandle(MAKELP(0,hInstance)); If you #include , you can instead use the macro GetInstanceModule(hInstance). Under Windows NT, just pass a NULL to obtain the handle of the current process. Note that the MSDN News article on this subject has the arguments to MAKELP() backwards. Reference: MSDN News #1, 1993 Revised by: Tom Nolan ------------------------------------------------------------ WDJ SDK Annotation #5 TYPE: Win3.1 TOPIC: CS_BYTEALIGNWINDOW 0x2000 KEYWORD: CS_BYTEALIGNWINDOW The documentation makes it sound like this style bit is the one you want for efficient bitblts. In fact, most of your bitblts will be to the client area of the window, not the non-client area, so CS_BYTEALIGNCLIENT is the style bit you should set if you are concerned about bitblt operation efficiency. Unaligned windows are slower at VGA resolution, but typically not an issue with higher resolution adapters (such as 256-color SVGA). Reference: p65, December 1993 Windows/DOS Developer's Journal. ------------------------------------------------------------ WDJ SDK Annotation #6 TYPE: Win3.1 TOPIC: CODE Module Definition Statement KEYWORD: CODE Module Definition Statement The documentation for the FIXED attribute is incorrect. Under Windows 3.1, if you mark code or data segments in your .exe FIXED, the loader ignores that attribute -- the segments will be moveable. If you mark code or data segments in your .dll FIXED, however, the loader will make them fixed and will page lock them as well. Due to the implementation of GlobalPageLock(), that can result in your segments using up precious DOS memory, eventually preventing Windows from spawning new applications (since each new application needs at least 512 bytes of DOS memory for a task database entry). The October 1994 Windows Developer's Journal provides code to allocate fixed memory without using up precious conventional memory. ------------------------------------------------------------ WDJ SDK Annotation #7 TYPE: Win3.1 TOPIC: WS_EX_TRANSPARENT 0x00000020L KEYWORD: WS_EX_TRANSPARENT Note that this bit does not really create transparent windows. If you create a window with this style, it is true that the windows below it will show through as its background. However, if you then move your new window, it will have the same background as it did in its original position -- blotting out whatever it is covering in its new position. ------------------------------------------------------------ WDJ SDK Annotation #8 TYPE: Win3.1 TOPIC: WM_NCHITTEST (2.x) KEYWORD: WM_NCHITTEST You can process this message to allow the user to drag a window that does not have a title bar. When you receive a WM_NCHITTEST message and the mouse is in your client area (or whatever conditions you want to start the drag), just return HTCAPTION rather than passing the message on to DefWindowProc(). Reference: p 37, March 1993 Windows/DOS Developer's Journal. ------------------------------------------------------------ WDJ SDK Annotation #9 TYPE: Win3.1 TOPIC: WM_TIMER (2.x) KEYWORD: WM_TIMER Although it sounds odd, you can use a WM_TIMER message as a way to kill another application, if you can obtain the handle of the main of the application you want to kill. Create a timer callback function that does nothing but pass its first argument (window handle) to DestroyWindow(). Then use PostMessage() to post (to the other app's main window) a WM_TIMER message that points to your callback function. Your timer callback will get executed in the context of the target application. Reference: p 64, September 1992 Windows/DOS Developer's Journal. ------------------------------------------------------------ WDJ SDK Annotation #10 TYPE: Win3.1 TOPIC: MemoryWrite (3.1) KEYWORD: MemoryWrite MemoryWrite() has a bug in it: it trashes the high 16 bits of the EDI register (the 32-bit version of the DI register). The workaround is to save the register before calling MemoryWrite() and restore it afterward. Reference: p. 71, April 1994 Windows/DOS Developer's Journal ------------------------------------------------------------ WDJ SDK Annotation #11 TYPE: Win3.1 TOPIC: EscapeCommFunction (2.x) KEYWORD: EscapeCommFunction The documentation omits one potential value for the nFunction parameter, although it is defined in windows.h. The name is GETBASEIRQ and it returns the base address of the COM port in the lower word and the IRQ setting in the high word. If the high word is -1 the port doesn't exist; if it is 0, the comm driver does not support this escape (which is the case, for example with some kinds of enhanced serial boards). Submitted by: Thomas Zeisluft ------------------------------------------------------------ WDJ SDK Annotation #12 TYPE: Win3.1 TOPIC: MessageBox (2.x) KEYWORD: MessageBox Do not call MessageBox() from within the LibMain() of an implicitly-linked DLL. It will fail because the application will not yet have a message queue at that point, and MessageBox() (or anything else) cannot create its window when the message queue does not yet exist. Once the application executes its internal startup code and calls InitApp(), then it has a message queue and can safely call functions that create windows. ------------------------------------------------------------ WDJ SDK Annotation #13 TYPE: Win3.1 TOPIC: LB_ADDSTRING (2.x) KEYWORD: LB_ADDSTRING If you are changing the contents of a listbox (for example, by adding or deleting multiple strings), you may want to minimize screen redrawing and maximize speed by disabling window redrawing during your operation. Follow these steps: 1) Send a WM_SETREDRAW with wParam equal to FALSE to the listbox. 2) Perform your adds or deletes. 3) Send a WM_SETREDRAW with wParam equal to TRUE to the listbox. 4) Use InvalidateRect() to force the listbox to redraw itself. Revised by V. Ramachandran ------------------------------------------------------------ WDJ SDK Annotation #14 TYPE: Win3.1 TOPIC: RegisterWindowMessage (2.x) KEYWORD: RegisterWindowMessage Do you really need to register a private window message? Probably not, if all you need is an intra-app message number that does not conflict with any Windows message numbers. Microsoft has revised its statement about what message numbers are available for your private use. Microsoft now guarantees that you can use message numbers 0x8000 through 0xBFFF and they will not conflict with any system messages. They also claim the next SDK (Chicago?) will define WM_APP equal to 0x8000 in windows.h. Reference: Microsoft Knowledge Base article Q86835 ------------------------------------------------------------ WDJ SDK Annotation #15 TYPE: Win3.1 TOPIC: WM_CHAR (2.x) KEYWORD: WM_CHAR The documentation incorrectly claims that wParam is the virtual key code. In fact, it is the ASCII value of the key pressed. For example, pressing the '$' (ASCII 0x5B) key produces a wParam equal to 0x5B -- if you interpreted that as a virtual key code, you would incorrectly believe that the user had pressed VK_HOME! Submitted by: Brent Rector ------------------------------------------------------------ WDJ SDK Annotation #16 TYPE: Win3.1 TOPIC: WinHelp (3.0) KEYWORD: WinHelp The documentation says that the return value is nonzero if WinHelp() is successful. In fact, WinHelp() only returns failure for systemic problems, like being unable to allocate global memory, or being unable to spawn winhelp.exe. If WinHelp() successfully passes your request to winhelp.exe, it returns success, period. So, for example, if the named help file is invalid, or you try to jump to a help topic that does not exist, or any number of other logical errors, WinHelp() still returns success. ------------------------------------------------------------ WDJ SDK Annotation #17 TYPE: Win3.1 TOPIC: WM_ENTERIDLE (2.x) KEYWORD: WM_ENTERIDLE The documentation says that this messsage gets sent to your application's "main window". In fact, a dialog sends the WM_ENTERIDLE message to its own parent window, which may or may not happen to be your application's main window. ------------------------------------------------------------ WDJ SDK Annotation #18 TYPE: Win3.1 TOPIC: OPENFILENAME (3.1) KEYWORD: OPENFILENAME The documentation does not completely describe the behavior when selecting multiple files. To allow the user to select multiple files, you turn on the flag OFN_ALLOWMULTISELECT. If you do that and call GetOpenFileName(), and if the user then selects multiple files, then GetOpenFileName() will copy (into lpstrFile) the path, followed by a space, followed by space-separated filenames. For example, if the user selected files "fred.1" and "fred.2" from directory "c:\test", lpstrFile would then point to the following string: "c:\test fred.1 fred.2" However, the documentation does not point out that if the user selects only one file, then the path is not kept separate from the filename. Using the previous example, if the user selected only file "fred.1", then lpstrFile would point to this: "c:\test\fred.1" Submitted by Julian Templeman ------------------------------------------------------------ WDJ SDK Annotation #19 TYPE: Win3.1 TOPIC: CreateRoundRectRgn (3.0) KEYWORD: CreateRoundRectRgn This function has a bug. It will produce a GP fault if the region rectangle is empty (either nLeftRect equals nRightRect, or nBottomRect equals nTopRect). About the only workaround is to create a wrapper function that first checks whether the rectangle is empty. Reference: p67, June 1994 Windows/DOS Developer's Journal^ Submitted by Chris Mason ------------------------------------------------------------ WDJ SDK Annotation #20 TYPE: Win3.1 TOPIC: EnableMenuItem (2.x) KEYWORD: EnableMenuItem When you are making changes to a window menu, the menu bar is not immediately updated. To force those changes (such as enabling/disabling menu items) to be visible right away, make sure you call DrawMenuBar(). ------------------------------------------------------------ WDJ SDK Annotation #21 TYPE: Win3.1 TOPIC: DrawText (2.x) KEYWORD: DrawText DrawText() has an off-by-one error that can result in a GP fault. The bug is evoked when you use pass an explicit string length instead of NULL-terminating the text string. The bug is not evoked if you use the DT_NOPREFIX flag, or if you NULL-terminate the text string (the easiest workaround). Reference: p53, August 1994 Windows/DOS Developer's Journal ------------------------------------------------------------ WDJ SDK Annotation #22 TYPE: Win3.1 TOPIC: WM_MEASUREITEM (3.0) KEYWORD: WM_MEASUREITEM Windows supports owner-draw menus, but only popup owner-draw menus work correctly. If you try to create an owner-draw menubar for a window, Windows will not send you the WM_MEASUREITEM message as it should. Reference: Microsoft Knowledge Base article Q69969 ------------------------------------------------------------ WDJ SDK Annotation #23 TYPE: Win3.1 TOPIC: WINDOWPOS (3.1) KEYWORD: WINDOWPOS The documentation says that y is "the position of the right edge of the window". It is, of course, the position of the top edge of the window. ------------------------------------------------------------ WDJ SDK Annotation #24 TYPE: Win3.1 TOPIC: Shell Dynamic-Data Exchange Interface Overview (3.1) KEYWORD: Shell Dynamic-Data Exchange Interface Overview The documentation says you can use DDE to get a list of Program Manager groups by "issuing a request for the Group item." In fact, that is not the correct item name -- you must use 'Groups', not 'Group'. ------------------------------------------------------------ WDJ SDK Annotation #25 TYPE: Win3.1 TOPIC: TrackPopupMenu (3.0) KEYWORD: TrackPopupMenu The documentation incorrectly states that you can pass the TPM_RIGHTBUTTON flag if you want the menu to respond to the right (secondary) mouse button instead of the left (primary) mouse button. In fact, passing TPM_RIGHTBUTTON causes the menu to respond to the right mouse button as well as the left. There is apparently no combination of bits that cause the menu to respond only to the right mouse button. ------------------------------------------------------------ WDJ SDK Annotation #26 TYPE: Win3.1 TOPIC: SetDlgItemText (2.x) KEYWORD: SetDlgItemText There is a bug in Windows that keeps SetWindowText() and SetDlgItemText() from working correctly when applied to an edit control owned by another application. Rather than sending a WM_SETTEXT to the edit control as they should, these functions directly tinker with the target control's internal window structure to change its title. The bug, then, is twofold: a) The target window is never notified that it needs to repaint. b) An edit control ignores its title, so changing its title does not affect the text it contains. The workaround is to use SendMessage() or PostMessage() to deliver a WM_SETTEXT to the target window. If you use PostMessage(), make sure you pass a string pointer that is somehow guaranteed to still be valid whenever the target application gets around to fetching and processing the message. Reference: p. 71, September 1993 Windows/DOS Developer's Journal ------------------------------------------------------------ WDJ SDK Annotation #27 TYPE: Win3.1 TOPIC: GetMenuItemID (2.x) KEYWORD: GetMenuItemID The documentation says that this function returns 0 if the specified menu item is a separator. In fact, although the resource compiler implicitly assigns separators an ID of 0, you can assign them any 16-bit ID you like (with ModifyMenu(), InsertMenu(), etc.) and this function will return the correct ID, not just zero. ------------------------------------------------------------ WDJ SDK Annotation #28 TYPE: Win3.1 TOPIC: GetMetaFile (2.x) KEYWORD: GetMetaFile Most programs that write a "Windows metafile" to disk use a newer file format that this function does not understand. Most programs read and write "placeable" metafiles, a metafile with a 22-byte header that contains a minimum bounding rectangle for the figure. If you want to read in a metafile that may have been written by another application, you should open the file yourself and check the header to see if it is a placeable metafile. See the SDK documentation for a description of "placeable Windows metafiles". Reference: p. 43, November 1994 Windows/DOS Developer's Journal ------------------------------------------------------------ WDJ SDK Annotation #29 TYPE: Win3.1 TOPIC: WritePrivateProfileString (3.0) KEYWORD: WritePrivateProfileString The documentation for WritePrivateProfileString() and the prototypes in windows.h, indicate that all strings passed in are LPCSTR (32-bit pointer to unmodifiable character string). However, sometimes this function writes on your input string anyway! For one example, if you pass in the string "Hello World " the function will remove the trailing spaces by writing a NULL byte after the 'd'. If you were passing in a constant string that resided in a code segment, this aberrant behavior could result in a GP fault. This function absolutely should not modify a string declared as LPCSTR, but since it does, beware! Submitted by Charles Leamon ------------------------------------------------------------ WDJ SDK Annotation #30 TYPE: Win3.1 TOPIC: GetMenuState (2.x) KEYWORD: GetMenuState In addition to the bits noted in the documentation, this function also correctly returns MF_POPUP for a submenu. Oddly, if you located the popup by position, the MF_BYPOSITION flag will also be set in the returned flags, although it is not on when used to locate normal menu items. Note that some of these flags equate to zero, so you cannot just AND them with the returned flags to see if they are on. Here are the flags that equate to zero, along with the expression you can use to infer their presence: Zero Flag Expression to test for flag ============== ================================= MF_ENABLED !(Flag&~(MF_DISABLED|MF_GRAYED)) MF_UNCHECKED !(Flag&~MF_CHECKED) MF_STRING !(Flag&~(MF_BITMAP|MF_OWNERDRAW)) ------------------------------------------------------------ WDJ SDK Annotation #31 TYPE: Win3.1 TOPIC: GetWindowPlacement (3.1) KEYWORD: GetWindowPlacement From the documentation, you might think the following code would work: WINDOWPLACEMENT Info; GetWindowPlacement(hWnd, &Info); In fact, it won't. You must remember to initialize the "length" field of the structure before calling this function. The following code works: WINDOWPLACEMENT Info; Info.length = sizeof(Info); GetWindowPlacement(hWnd, &Info); Submitted by Pete Davis ------------------------------------------------------------ WDJ SDK Annotation #32 TYPE: Win3.1 TOPIC: CreateCompatibleBitmap (2.x) KEYWORD: CreateCompatibleBitmap The description of height and width parameters states that these values are in bits, when actually they are in pixels. Submitted by Charles Leamon ------------------------------------------------------------ WDJ SDK Annotation #33 TYPE: Win3.1 TOPIC: LoadCursor (2.x) KEYWORD: LoadCursor The documentation says that you should call DestroyCursor() for cursors loaded via LoadCursor(). That is wrong -- you should only call DestroyCursor() for cursors created with CreateCursor(). Submitted by Charles Leamon^ Reference: Microsoft Knowledge Base article Q84779 ------------------------------------------------------------ WDJ SDK Annotation #34 TYPE: Win3.1 TOPIC: lstrcpyn (3.1) KEYWORD: lstrcpyn The documentation for lstrcpyn() states that the last parameter (cChars) is the number of characters to be copied, when in fact the number of characters will be cChars-1. That's convenient, but it's not what it says and it's inconsistent with the standard ANSI C run-time function strncpy(), which does copy the specified count. In other words, unlike strncpy(), this function always NULL-terminates the destination string. Submitted by Charles Leamon ------------------------------------------------------------ WDJ SDK Annotation #36 TYPE: Win3.1 TOPIC: ExitWindows (3.0) KEYWORD: ExitWindows The documentation is incomplete. To just terminate Windows and return control to DOS, pass a zero in the dwReturnCode parameter. Submitted by Charles Leamon^ Reference: Microsoft Knowledge Base article Q100359 ------------------------------------------------------------ WDJ SDK Annotation #37 TYPE: Win3.1 TOPIC: OpenFile (2.x) KEYWORD: OpenFile The descriptions for OF_CANCEL and OF_PROMPT are incorrect. OF_CANCEL does not add a cancel button to the 'File not found' (OF_PROMPT) dialog. Even if it did, how would the caller know the user pressed the cancel button (only one error return is defined for OpenFile())? The OF_PROMPT dialog does not prompt the user to insert a diskette into drive A: Submitted by Charles Leamon ------------------------------------------------------------ WDJ SDK Annotation #38 TYPE: Win3.1 TOPIC: WM_NCHITTEST (2.x) KEYWORD: WM_NCHITTEST The documentation claims that this message is sent to the window that used SetCapture() to capture mouse input. That is totally false. The window whose handle is passed to SetCapture() will never receive any WM_NCHITTEST messages (no matter where you move the moust) while the mouse is captured. Submitted by V. Ramachandran ------------------------------------------------------------ WDJ SDK Annotation #39 TYPE: Win3.1 TOPIC: TabbedTextOut (3.0) KEYWORD: TabbedTextOut The documentation claims that the tab stops are in device units (pixels), but that is not true. The tab stops are treated as logical units, not device units. Submitted by Dan Miser^ Reference: Microsoft Knowledge Base article Q113253 ------------------------------------------------------------ WDJ SDK Annotation #40 TYPE: Win3.1 TOPIC: LoadIcon (2.x) KEYWORD: LoadIcon The documentation says that you should call DestroyIcon() for cursors loaded via LoadIcon(). That is wrong -- you should only call DestroyIcon() for icons created with CreateIcon(). Submitted by Charles Leamon^ Reference: Microsoft Knowledge Base article Q84779 ------------------------------------------------------------ WDJ SDK Annotation #41 TYPE: Win3.1 TOPIC: COMPAREITEMSTRUCT (3.0) KEYWORD: COMPAREITEMSTRUCT Note that Windows has to send a WM_COMPAREITEM message when a new item is added to the list, in order to determine its correct position. That means it does not know the position of the new item yet, so (contrary to the documentation) the itemID2 field in this structure will be -1 -- do not assume it will be a legal index value. Submitted by V. Ramachandran ------------------------------------------------------------ WDJ SDK Annotation #42 TYPE: Win3.1 TOPIC: GetProcAddress (2.x) KEYWORD: GetProcAddress In attempting to locate the named function in the target module, GetProcAddress() converts the function name to uppercase and then performs a case-sensitive search. That means that GetProcAddress() cannot locate functions exported with names containing lowercase characters. Normally, that's not a problem, as the __pascal calling sequence forces uppercase names. However, depending on the compiler and linker tools and options you use, it is possible to export __cdecl calling sequence functions with lowercase characters, resulting in a function that GetProcAddress() cannot locate. Submitted by Keith Bluestone ------------------------------------------------------------ WDJ SDK Annotation #43 TYPE: Win3.1 TOPIC: SetScrollRange (2.x) KEYWORD: SetScrollRange The documentation says you can use this function to hide or show standard scroll bars, but does not tell you how! Basically, if you specify the same value for both the minimum (nMin) and maximum (nMax) scrolling positions, the function will hide the scroll bar. If the two positions are not equal, the function will display the scroll bar. Submitted by Paul Bonneau ------------------------------------------------------------ WDJ SDK Annotation #44 TYPE: Win3.1 TOPIC: LoadLibrary (2.x) KEYWORD: LoadLibrary If LoadLibrary() cannot find the library, it may display an error message to the user, depending upon the state of Windows' "error mode". If you want to handle that case yourself and make sure Windows does not display the error message, see the documentation for the function SetErrorMode(). For example, the following code attempts to load the library ctl3d.dll, but does not emit an error message if it is not found. HINSTANCE Ctl3d;^ UINT OldFlag = SetErrorMode(SEM_NOOPENFILEERRORBOX);^ Ctl3d = LoadLibrary("ctl3d.dll");^ SetErrorMode(OldFlag); // restore previous mode^ if(Ctl3d <= HINSTANCE_ERROR)^ // LoadLibrary() failed for some reason^ ------------------------------------------------------------ WDJ SDK Annotation #45 TYPE: Win3.1 TOPIC: LoadLibrary (2.x) KEYWORD: LoadLibrary The documentation claims this function returns an error code of 0 if "System was out of memory, executable file was corrupt, or relocations were invalid". However, if a library's LibMain() function returns 0 (signifying some logical error during initialization), LoadLibrary() also returns 0. Therefore, do not assume that a 0 error code means the system was out of memory or that the module was corrupt in some way. ------------------------------------------------------------ WDJ SDK Annotation #46 TYPE: Win3.1 TOPIC: DdeClientTransaction (3.1) KEYWORD: DdeClientTransaction The documentation does not say so, but the cbData argument (length of data) must include the NULL byte if the data is a string. In other words, if lpvData is a string, cbData must be set to strlen(lpvData)+1. Otherwise, bad things may happen in DDEML when you perform an XTYP_POKE or XTYP_EXECUTE. Submitted by Mark Reha^ Reference: Microsoft Knowledge Base article Q107387. ------------------------------------------------------------ WDJ SDK Annotation #47 TYPE: Win3.1 TOPIC: WinHelp (3.0) KEYWORD: WinHelp The WinHelp() API function normally allows one to execute macros, jumps, popups, and so on. However, if WinHelp was started with WinExec() (i.e. from Program Manager or File Manager) instead of the WinHelp() API function, you will not be able to execute macros or jumps on that help file without starting up a second instance of the help file using the WinHelp() API function. There is one way around this. You can create a DLL with an LDLLHandler that gets the callback address for the FAPI() function from WinHelp. FAPI() has the same parameters as the WinHelp() API function except that the first parameters (HWND) is not in FAPI() (so FAPI() only has 3 parameters). The FAPI() function will allow you to execute macros, jumps, popups, etc regardless of how WinHelp was launched. For more information, get the Windows Help Authors Guide from the MSDN CD-ROM or see Jim Mischel's book "The Developer's Guide to WINHELP.EXE". Submitted by Pete Davis ------------------------------------------------------------ WDJ SDK Annotation #48 TYPE: Win3.1 TOPIC: _fpmath (2.x) KEYWORD: _fpmath When setting the handler for coprocessor error exceptions (function 3), the documentation incorrectly says you should place the address of your exception handler in DS:AX. The correct registers to use for this 32-bit address are DX:AX. Submitted by Manfred Keul ------------------------------------------------------------ WDJ SDK Annotation #49 TYPE: Win3.1 TOPIC: VerQueryValue (3.1) KEYWORD: VerQueryValue Amazingly, even though the second parameter to this function is declared const (LPCSTR), VerQueryValue() modifies that string anyway! Apparently the code replaces a '\' in your string with a NULL byte temporarily and then puts it back. This is a bug. For example, suppose you use a compiler option that places constant strings in read-only code segments (for Microsoft, "/Gf"; for Borland "-dc"). In that case, passing such a constant string as the second argument to this function results in a GP fault. Submitted by David Lowndes ------------------------------------------------------------ WDJ SDK Annotation #50 TYPE: Win3.1 TOPIC: GetProfileString (2.x) KEYWORD: GetProfileString This applies to both GetProfileString() and GetPrivateProfileString(). If the default value for these functions contains trailing blanks and the default value is used because the key did not appear in the INI file, Windows will null-terminate the string (even though it is declared const!) at the first trailing blank. If that string is a literal string and the code is compiled with some optimizations then the string will end up in a code segment, resulting in a GPF when the API function attempts to modify it. GetProfileString() and GetPrivateProfileString() also strip out any leading spaces, and any leading and trailing quotes (single or double quotes). Submitted by Michael E. Kropp.^ Revised by Kai Riihioja ------------------------------------------------------------ WDJ SDK Annotation #50 TYPE: Win3.1 TOPIC: GetPrivateProfileString (2.x) KEYWORD: GetPrivateProfileString This applies to both GetProfileString() and GetPrivateProfileString(). If the default value for these functions contains trailing blanks and the default value is used because the key did not appear in the INI file, Windows will null-terminate the string (even though it is declared const!) at the first trailing blank. If that string is a literal string and the code is compiled with some optimizations then the string will end up in a code segment, resulting in a GPF when the API function attempts to modify it. GetProfileString() and GetPrivateProfileString() also strip out any leading spaces, and any leading and trailing quotes (single or double quotes). Submitted by Michael E. Kropp.^ Revised by Kai Riihioja ------------------------------------------------------------ WDJ SDK Annotation #51 TYPE: Win3.1 TOPIC: GetTickCount (2.x) KEYWORD: GetTickCount GetTickCount() may return units of milliseconds, but its resolution is much worse than one millisecond under Windows 3.1. For more accurate timings, call the function timeGetTime(), which is defined in mmsystem.h. ------------------------------------------------------------ WDJ SDK Annotation #52 TYPE: Win3.1 TOPIC: GetWinFlags (3.0) KEYWORD: GetWinFlags GetWinFlags() can also tell you if your 16-bit Windows 3.1 application is running under Windows NT: if(GetWinFlags() & 0x04000)^ // then we are running under Windows NT^ Submitted by Paula Tomlinson.^ Reference: "The Ultimate Windows Version Detector", Windows/DOS Developer's Journal, February 1995. ------------------------------------------------------------ WDJ SDK Annotation #53 TYPE: Win3.1 TOPIC: KillTimer (2.x) KEYWORD: KillTimer Under at least one condition, KillTimer() does not remove a pending WM_TIMER message from the message queue as documented. First, understand that Windows just sets a bit when a timer fires; it does not generate a WM_TIMER message at that time. A timer message is secretly added to your input queue when you call GetMessage() or PeekMessage() and a timer event is pending and no other messages are in the queue. If you are using a PeekMessage() call with the PM_NOREMOVE flag and if the timer bit is on at that point, PeekMessage() will place a WM_TIMER message in the queue, but won't remove it. If you then call KillTimer(), it will ensure the timer bit is off but won't remove the WM_TIMER message. The next call to GetMessage() or PeekMessage() returns this WM_TIMER message. One workaround is to use code like this to terminate a timer: KillTimer (hWnd, ID);^ if (LOWORD (GetQueueStatus (QS_TIMER)) & QS_TIMER)^ PeekMessage (&msg, hWnd, WM_TIMER, WM_TIMER, PM_REMOVE);^ Submitted by Mike Mast. ------------------------------------------------------------ WDJ SDK Annotation #54 TYPE: Win3.1 TOPIC: DEVMODE (3.0) KEYWORD: DEVMODE The documentation claims that DMCOLOR_COLOR is defined to be 1 and DMCOLOR_MONOCHROME is defined to be 2. In fact, if you look in print.h you discover that the reverse is true. Submitted by Bill Liu. ------------------------------------------------------------ WDJ SDK Annotation #55 TYPE: Win3.1 TOPIC: EN_CHANGE (2.x) KEYWORD: EN_CHANGE The documentation implies this notification only arises from actions by the user. In fact, this notification also arises from programmatic changes, such as from sending a WM_SETTEXT message to the edit control, or using SetWindowText() (which sends a WM_SETTEXT message). Not knowing this, you might code an EN_CHANGE handler that attempts to modify the edit control text, resulting in another EN_CHANGE notification -- an infinite loop! Submitted by Scott Smith. ------------------------------------------------------------ WDJ SDK Annotation #56 TYPE: Win3.1 TOPIC: EnumFonts (2.x) KEYWORD: EnumFonts The 3.1 documentation for this function specifies that the third argument is of type FONTENUMPROC. This was true in previous versions, but in 3.1 this call is deprecated in favour of the new EnumFontFamilies() API function. The definition of the FONTENUMPROC type has been updated to take a NEWTEXTMETRIC parameter, and therefore no longer quite matches the prototype specified for EnumFonts. Microsoft provids a new type, OLDFONTENUMPROC, which corresponds to the STRICT definition of EnumFonts in windows.h, but this is not reflected in the documentation. In other words, if you compile with STRICT defined for Windows 3.1 and want to use EnumFonts(), make sure the third parameter is of type OLDFONTENUMPROC. Submitted by David W. Gillett. ------------------------------------------------------------ WDJ SDK Annotation #57 TYPE: Win3.1 TOPIC: PtInRect (2.x) KEYWORD: PtInRect There is no mention of this, but the rectangle MUST be normailized before this function is called. In other words, you have to make sure that lprc->right is greater than lprc->left, and that lrpc->bottom is greater than lrpc->top. Otherwise, the point will never be considered inside of the rectangle. By contrast, the function RectInRegion() does accept and correctly handle all rectangles, whether normalized or not. Submitted by Peter Ritchie. ------------------------------------------------------------ WDJ SDK Annotation #58 TYPE: Win3.1 TOPIC: SetTimer (2.x) KEYWORD: SetTimer The documentation makes it sound like Windows either posts a message (if you supply no callback function) or else calls your callback function directly when the timer expires. In fact, when the timer fires, Windows sets a bit in your message queue which gets transformed into a WM_TIMER message by either GetMessage() or PeekMessage() when they find no other messages in the input queue. The WM_TIMER message contains the address of your callback function (if any), which will be called by DefWindowProc() after the message is dispatched to your window. The key point here is that the timers created by SetTimer() are always message based (Windows does not call your timer procedure asynchronously) and of a lower priority than any other Windows message. Submitted by Alan M. Carroll. ------------------------------------------------------------ WDJ SDK Annotation #59 TYPE: Win3.1 TOPIC: lstrcpy (2.x) KEYWORD: lstrcpy Believe it or not, this function (and apparently other similar functions) examine the limit of the selector of the output string, and even attempt to silently recover if the operation causes a GP fault. As a consequence, you should not count on this function being a real speed demon. Submitted by Vivek Venugopalan ------------------------------------------------------------ WDJ SDK Annotation #60 TYPE: Win3.1 TOPIC: GetModuleFileName (2.x) KEYWORD: GetModuleFileName Windows 3.1 has a bug that causes this function to sometimes return relative paths instead of absolute (fully qualified) paths.This error occurs if a relative path is specified in the PATH variable, and the DLL is implicitly loaded from this directory. For example, if the PATH variable is: PATH=C:\DOS;D:.;C:\UTILS and an application running from any other directory but "D:.", loads a DLL (test.dll) in "D:." implicitly (since it is in the path), then a call to GetModuleFileName() with the DLL instance will return "D:.\test.dll". Submitted by V. Ramachandran Reference: MSKB PSS ID Number: Q85330. ------------------------------------------------------------ WDJ SDK Annotation #61 TYPE: Win3.1 TOPIC: CB_GETDROPPEDCONTROLRECT (3.1) KEYWORD: CB_GETDROPPEDCONTROLRECT The documentation claims that this function retrieves the screen coordinates of the listbox portion of a combo box. In fact, it retrieves the screen coordinates of the rectangle that encloses the ENTIRE combo box in its dropped-down state. That means the rectangle retrieved is both bit taller and a bit wider than the rectangle that the help file claims is returned. Submitted by V. Ramachandran ------------------------------------------------------------ WDJ SDK Annotation #62 TYPE: Win3.1 TOPIC: NotifyUnRegister (3.1) KEYWORD: NotifyUnRegister As the documentation says, you can set the htask argument to NULL to refer to the current task. This is probably not a good practice, however. If more than one application can load your DLL, then there is typically some scenario under which the task that you called NotifyRegister() for has died before you call NotifyUnRegister(), in which case passing NULL would refer to the wrong task. It's probably safer to explicitly store that task that was passed to NotifyRegister() and make sure you pass the same task to NotifyUnregister(). Submitted by Paul Dolphin. ------------------------------------------------------------ WDJ SDK Annotation #63 TYPE: Win3.1 TOPIC: CreateCompatibleDC (2.x) KEYWORD: CreateCompatibleDC You might think from the name that this function returns a device context whose attributes are the same as the source device context. In fact, attributes such as the mapping mode will be set to their defaults (e.g., the mapping mode will always be MM_TEXT) in the returned device context, not to the attribute values of the source device context. Submitted by Stuart Patterson^ Reference: p. 624, "Programming Windows 3.1, 3rd Edition", by Charles Petzold ------------------------------------------------------------ WDJ SDK Annotation #64 TYPE: Win3.1 TOPIC: Device Contexts KEYWORD: Device Contexts The help file says CreateCompatibleDC() creates a device context that "has the same attributes" as the source device context. In fact, the device context's attributes (such as mapping mode) will have their default values, no matter what value they had in the source device context. Submitted by Stuart Patterson^ Reference: p. 624, "Programming Windows 3.1, 3rd Edition", by Charles Petzold ------------------------------------------------------------ WDJ SDK Annotation #65 TYPE: Win3.1 TOPIC: UngetCommChar (2.x) KEYWORD: UngetCommChar Do not use this function under Windows 3.1 -- it causes lost characters or even GP faults! Submitted by Manfred Keul.^ Reference: Microsoft Knowledge Base article Q100183. ------------------------------------------------------------ WDJ SDK Annotation #66 TYPE: Win3.1 TOPIC: TabbedTextOut (3.0) KEYWORD: TabbedTextOut If a tab character is the last character in the string, then all of the area to the next tab stop is filled with the current background color. This may or may not be the behavior you want in any given situation. Submitted by Tim English. ------------------------------------------------------------ WDJ SDK Annotation #67 TYPE: Win3.1 TOPIC: GetRgnBox (3.0) KEYWORD: GetRgnBox The documentation claims GetRgnBox() returns COMPLEXREGION when the region has overlapping borders. In fact, GetRgnBox() apparently returns COMPLEXREGION if the region is simply non-rectangular, whether overlapping borders are involved or not. Submitted by Jason Douglas. ------------------------------------------------------------ WDJ SDK Annotation #68 TYPE: Win3.1 TOPIC: DRAWITEMSTRUCT (3.0) KEYWORD: DRAWITEMSTRUCT The itemID field in this structure is set to a negative value for an empty listbox or combobox. Watch out, though -- since this field is defined to be unsigned (UINT), a statement like this: if(lpdis->itemID >= 0) // if listbox not empty^ // ... some code^ will always evaluate true. To check for an empty listbox or combobox, either cast the field to int or check the high bit: if( (int) lpdis->itemID >= 0) // this works correctly^ //... some code^ Submitted by Aaron O'Neil. ------------------------------------------------------------ WDJ SDK Annotation #69 TYPE: Win3.1 TOPIC: DDEDATA (2.x) KEYWORD: DDEDATA In this help topic, the description for the fResponse field actually describes the fAckReq field, and vice versa. Submitted by Sudhir Menon.^ Reference: Microsoft Knowledge Base article Q93372. ------------------------------------------------------------ WDJ SDK Annotation #70 TYPE: Win3.1 TOPIC: EnableCommNotification (3.1) KEYWORD: EnableCommNotification Due to bugs in Windows 3.1, you will probably want to always set both cbWriteNotify and cbOutQueue to -1, thus disabling the CN_TRANSMIT and CN_RECEIVE notifications. If you do not set them to -1, spurious WM_COMMNOTIFY messages can be sent, resulting in a system crash at higher baud rates. Submitted by Manfred Keul.^ Reference: Microsoft Knowledge Base article Q101420. ------------------------------------------------------------ WDJ SDK Annotation #71 TYPE: Win3.1 TOPIC: GetMsgProc (3.1) KEYWORD: GetMsgProc The help file fails to mention that your hook function gets called by PeekMessage(), not just by GetMessage(). The documentation also contradicts itself, saying first that wParam is undefined and later saying that wParam is NULL. The Microsoft Knowledge Base, on the other hand, reveals that wParam contains the PM_ flags that were used in the call to PeekMessage(), so your message hook can, for example, determine if the message was being removed or not with code like this: if(wParam & PM_REMOVE)^ //... then message is being removed^ else^ //... message is not being removed^ Note that your hook may want to ignore the message if it is not being removed, since your hook will get called again when the same message is removed by some future call to GetMessage() or PeekMessage(). Submitted by V. Ramachandran.^ Reference: Microsoft Knowledge Base article Q104068 ------------------------------------------------------------ WDJ SDK Annotation #72 TYPE: Win3.1 TOPIC: GetInstanceData (2.x) KEYWORD: GetInstanceData Both the documentation and windows.h declare the second parameter as a BYTE*. Unfortunately, that declaration is only correct if you are using a memory model with near data (small or medium memory models), and will be incorrect for large or huge memory models. The correct declaration for this argument is BYTE NEAR*. Submitted by Martin Cooper. ------------------------------------------------------------ WDJ SDK Annotation #73 TYPE: Win3.1 TOPIC: MENUITEMTEMPLATE (3.0) KEYWORD: MENUITEMTEMPLATE Missing from the list of bits you can turn on in mtOption is MF_END (0x0080), which indicates that the item terminates the menu. Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #74 TYPE: Win3.1 TOPIC: AddAtom (2.x) KEYWORD: AddAtom AddAtom() handles strings that begin with "#" specially: it expects the string following the "#" to be a string of digits, and returns an atom is value is the 16-bit binary representation of that string of digits. Unfortunately, if your first call to AddAtom() is with a string like "#nondigits", it will produce a divide by zero fault. Two workarounds are possible: either make sure your first call to AddAtom() does not contain such a string, or call InitAtomTable() before calling AddAtom() for the first time. Submitted by V. Ramachandran.^ Reference: MSKB PSS ID Number: Q103036 ------------------------------------------------------------ WDJ SDK Annotation #75 TYPE: Win3.1 TOPIC: DCB (2.x) KEYWORD: DCB When setting the BaudRate field, do not use the constant CBR_14400; Windows 3.1's COMM.DRV has a bug that will produce communications problems due to a bad table entry for that constant. Instead, set BaudRate to the integer 14400 to communicate at 14400 baud. Submitted by Manfred Keul.^ Reference: Microsoft Knowledge Base article Q83232. ------------------------------------------------------------ WDJ SDK Annotation #76 TYPE: Win3.1 TOPIC: RegisterRoutine WinHelp macro KEYWORD: RegisterRoutine WinHelp macro The documentation does not reveal how to specify the return type of the function. You do this by inserting a type-specifying character followed by an equal sign in the third parameter string. For example, to register FindWindow() (which takes two far strings and returns a 16-bit unsigned integer), you might use: RR("USER", "FindWindow", "u=SS"); Note that if you do not specify a return type when you register a function, you cannot use that function in an IfThen or IfThenElse macro. Submitted by Sudhir Menon. ------------------------------------------------------------ WDJ SDK Annotation #77 TYPE: Win3.1 TOPIC: RTF Tokens KEYWORD: RTF Tokens Strangely, the tokens "emc", "eml", and "emr" are misspelled in the online help -- they should be "ewc", "ewl", and "ewr", where the "ew" stands for Embedded Window. Note that this are not really RTF tokens, but literal text. The help compiler scans for any text you have entered of the form "{ewx commands}" in order to detect embedded window commands. Such text, when translated by your word processor into RTF, looks like this: "\{ewx commands\}". Submitted by John Sawyer. ------------------------------------------------------------ WDJ SDK Annotation #78 TYPE: Win3.1 TOPIC: WM_ENTERIDLE (2.x) KEYWORD: WM_ENTERIDLE Note that you can elect to suppress the WM_ENTERIDLE message for a particular modal dialog box by defining it with the DS_NOIDLEMSG window style bit. Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #79 TYPE: Win3.1 TOPIC: WM_SYSCOMMAND (2.x) KEYWORD: WM_SYSCOMMAND You can use this message with SC_MENUKEY to simulate the user selecting a menu with an accelerator key. For example, to simulate the user accessing the "File" menu, you might use the following code: PostMessage(hWnd, WM_SYSCOMMAND, SC_MENUKEY, MAKELPARAM('f',0)); Submitted by Jay Giganti. ------------------------------------------------------------ WDJ SDK Annotation #80 TYPE: Win3.1 TOPIC: wsprintf KEYWORD: wsprintf The documentation says the second parameter is an LPSTR, but it is actually an LPCSTR (and so declared in windows.h), so it's safe to use a string constant. Submitted by: V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #81 TYPE: Win3.1 TOPIC: _lread (2.x) KEYWORD: _lread If you call _lread() to read a floppy when there is no diskette in the drive, Windows puts up a system error message box ("Cannot Read from Drive...") with Retry and Cancel buttons. If the user presses the Cancel button, _lread() returns a non-negative number, indicating success. It should return HFILE_ERROR instead. To avoid this error, call SetErrorMode(SEM_NOOPENFILEERRORBOX) before calling _lread(), then it will correctly return -1 on failure. Submitted by: V. Ramachandran.^ Reference: MSDN PSS ID No. Q111587. ------------------------------------------------------------ WDJ SDK Annotation #82 TYPE: Win3.1 TOPIC: WM_COMPAREITEM (3.0) KEYWORD: WM_COMPAREITEM The documentation for WM_COMPAREITEM says that the parent of owner-drawn listboxes with the LBS_SORT (or CBS_SORT) styles will get this message in order to determine the relative position. However, Windows will not send the WM_COMPAREITEM message if the LBS_HASSTRINGS style bit is set, even if it is an owner-drawn listbox with the LBS_SORT style. The same is true for comboboxes. Submitted by: V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #83 TYPE: Win3.1 TOPIC: DOCINFO (3.1) KEYWORD: DOCINFO The documentation fails to point out that lpszOutput is limited to 32 characters, including the null terminating byte. If you use a string longer than that, the trailing characters will be ignored. For example, if you use a string containing the too-long path: c:\usr\ts\issues\pending\12345678\abcdefgh.out the actual file that gets created will be: c:\usr\ts\issues\pending\123456 Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #84 TYPE: Win3.1 TOPIC: SetAbortProc function KEYWORD: SetAbortProc SetAbortProc() returns a negative value, which is documented as indicating failure. However, the return value from SetAbortProc does not indicate success or failure of the function, so don't depend on the return value for anything. Submitted by: V. Ramachandran.^ Reference: MSDN PSS ID No. Q109540. ------------------------------------------------------------ WDJ SDK Annotation #85 TYPE: Win3.1 TOPIC: BN_DISABLE (2.x) KEYWORD: BN_DISABLE The documentation claims the button sends this notification whenever it gets disabled. In fact, Windows 3.1 buttons do not appear to ever send this notification! Submitted by: V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #86 TYPE: Win3.1 TOPIC: BN_DOUBLECLICKED (2.x) KEYWORD: BN_DOUBLECLICKED The documentation fails to point out that this notification is only sent for buttons that have the BS_OWNERDRAW or BS_RADIOBUTTON styles. No other types of buttons generate this notification Submitted by: V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #87 TYPE: Win3.1 TOPIC: WM_DROPFILES (3.1) KEYWORD: WM_DROPFILES Documentation for the undocumented handle has since been published by Microsoft in Microsoft Systems Journal. This handle points to a structure like this: typedef struct {^ int wSize; // Number of bytes in this structure^ POINT ptMousePos; // Mouse position^ BOOL fInNonClientArea;// TRUE if mouse was in client area^ // Pathnames begin after structure each one zero-terminated^ // Zero-length pathname used to indicate the end^ } DROPFILESTRUCT, FAR *LPDROPFILESTRUCT;^ The Win32 version of this structure is slightly different. The first field becomes a 4-byte rather than a 2-byte integer, and the structure contains an additional BOOL field at the end that is TRUE if the pathnames are in Unicode rather than ANSI strings. Submitted by V. Ramachandran.^ Reference: May/June 1992 Microsoft Systems Journal^ February 1994 Microsoft Systems Journal ------------------------------------------------------------ WDJ SDK Annotation #87 TYPE: Win32 TOPIC: WM_DROPFILES KEYWORD: WM_DROPFILES AND Parameters Documentation for the undocumented handle has since been published by Microsoft in Microsoft Systems Journal. This handle points to a structure like this: typedef struct {^ int wSize; // Number of bytes in this structure^ POINT ptMousePos; // Mouse position^ BOOL fInNonClientArea;// TRUE if mouse was in client area^ // Pathnames begin after structure each one zero-terminated^ // Zero-length pathname used to indicate the end^ } DROPFILESTRUCT, FAR *LPDROPFILESTRUCT;^ The Win32 version of this structure is slightly different. The first field becomes a 4-byte rather than a 2-byte integer, and the structure contains an additional BOOL field at the end that is TRUE if the pathnames are in Unicode rather than ANSI strings. Submitted by V. Ramachandran.^ Reference: May/June 1992 Microsoft Systems Journal^ February 1994 Microsoft Systems Journal ------------------------------------------------------------ WDJ SDK Annotation #88 TYPE: Win3.1 TOPIC: List box messages KEYWORD: List box messages Internally, listboxes maintain two 32-bit DWORDs for each listbox item. The first DWORD points to the text for the item and the second DWORD contains whatever custom data you would like; you can get it via LB_GETITEMDATA or set it via LB_SETITEMDATA. Some messages refer to one or the other of these DWORDs, depending on whether the LBS_HASSTRINGS style is set: Message LBS_HASSTRINGS? Refers to:^ ==========================================================^ LB_ADDSTRING Yes text pointer (lParam)^ LB_ADDSTRING No custom data (lParam)^ LB_INSERTSTRING Yes text pointer (lParam)^ LB_INSERTSTRING No custom data (lParam)^ LB_GETTEXT Yes returns text pointer^ LB_GETTEXT No returns custom data^ LB_GETITEMDATA either returns custom data^ LB_SETITEMDATA either sets custom data (lParam)^ WM_DRAWITEM either custom data (in itemData field)^ In other words, if LBS_HASSTRINGS is not set, you cannot access the normal text pointer -- all messages operate on the custom data. But if LBS_HASSTRINGS is set, you can access both the normal text as well as the extra DWORD of custom data. Submitted by V. Ramachandran ------------------------------------------------------------ WDJ SDK Annotation #89 TYPE: Win3.1 TOPIC: SetTimer (2.x) KEYWORD: SetTimer Though the documentation does not mention this, you can change the timer interval after you create the timer. If you call SetTimer() with a window handle and a timer ID equal to an existing timer and specify a different time interval, SetTimer() updates the timer to the new time interval. Example: Use the following line to set a timer to a particular time interval. UINT nTimerID = SetTimer (hWnd, TIMER_ID, TIME_INTERVAL, NULL) Calling the following line will reset the timer to twice the time interval. nTimerID = SetTimer (hWnd, TIMER_ID, 2*TIME_INTERVAL, NULL) Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #90 TYPE: Win3.1 TOPIC: TranslateAccelerator (2.x) KEYWORD: TranslateAccelerator Contrary to the documentation, both WM_INITMENU and WM_INITMENUPOPUP get sent, even if the menu item is disabled, and even if the window is minimized and the keystroke matches no menu items. Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #91 TYPE: Win3.1 TOPIC: GetPrivateProfileInt (3.0) KEYWORD: GetPrivateProfileInt The documentation says you must use a positive integer in the range 0 through 32,767 (0x7FFF) for the third argument, which is the default value the function returns if it cannot locate the desired entry in the .ini file. In fact, you can pass any legal integer value for this parameter -- it's just that since the function's return type is defined to be UINT, you must cast the result to int if you want to treat the result as a negative number. For example, the following code works correctly, despite what the documentation says: int Val = (int)GetPrivateProfileInt( "MyLib", "Debug", -1, "MyLib.ini"); if(Val == -1) /* then no such entry found */ ------------------------------------------------------------ WDJ SDK Annotation #92 TYPE: Win32 TOPIC: WM_NOTIFY KEYWORD: WM_NOTIFY AND "See also" If your dialog procedure handles a WM_NOTIFY that requires a return value, note that you MUST both return a non-zero value (to indicate to the dialog manager that you wish to specify a return value for the message) AND store the desired return value for the message in the window field DWL_MSGRESULT. The standard Windows header file windowsx.h provides a macro called SetDlgMsgResult() that makes this easy: #include //... case WM_NOTIFY : { NMHDR* Head = (NMHDR*)lParam; if(Head->code == LVN_ENDLABELEDIT) // allow user to edit listview labels return SetDlgMsgResult(Dialog, WM_NOTIFY, TRUE); } ------------------------------------------------------------ WDJ SDK Annotation #92 TYPE: Win32 TOPIC: LVN_ENDLABELEDIT KEYWORD: LVN_ENDLABELEDIT AND Parameters If your dialog procedure handles a WM_NOTIFY that requires a return value (as this one does), note that you MUST both return a non-zero value (to indicate to the dialog manager that you wish to specify a return value for the message) AND store the desired return value for the message in the window field DWL_MSGRESULT. The standard Windows header file windowsx.h provides a macro called SetDlgMsgResult() that makes this easy: #include //... case WM_NOTIFY : { NMHDR* Head = (NMHDR*)lParam; if(Head->code == LVN_ENDLABELEDIT) // allow user to edit listview labels return SetDlgMsgResult(Dialog, WM_NOTIFY, TRUE); } ------------------------------------------------------------ WDJ SDK Annotation #92 TYPE: Win32 TOPIC: LVN_BEGINLABELEDIT KEYWORD: LVN_BEGINLABELEDIT AND Parameters If your dialog procedure handles a WM_NOTIFY that requires a return value (as this one does), note that you MUST both return a non-zero value (to indicate to the dialog manager that you wish to specify a return value for the message) AND store the desired return value for the message in the window field DWL_MSGRESULT. The standard Windows header file windowsx.h provides a macro called SetDlgMsgResult() that makes this easy: #include //... case WM_NOTIFY : { NMHDR* Head = (NMHDR*)lParam; if(Head->code == LVN_BEGINLABELEDIT) // allow user to edit listview labels return SetDlgMsgResult(Dialog, WM_NOTIFY, FALSE); } ------------------------------------------------------------ WDJ SDK Annotation #93 TYPE: Win3.1 TOPIC: GetWindowText KEYWORD: GetWindowText GetWindowText() and GetDlgItemText() will not work when applied to a standard edit control that belongs to another application. The problem is that GetWindowText() and GetDlgItemText() attempt to optimize by directly examining the window caption of the target window. An edit control stores an empty string in its caption, not the edit control text, so this fails. You can avoid this bug by sending a WM_GETTEXT explicitly: HWND OtherEdit; char Buffer[256]; SendMessage(OtherEdit, WM_GETTEXT, sizeof(Buffer), (LPARAM)Buffer); ------------------------------------------------------------ WDJ SDK Annotation #93 TYPE: Win3.1 TOPIC: GetDlgItemText KEYWORD: GetDlgItemText GetWindowText() and GetDlgItemText() will not work when applied to a standard edit control that belongs to another application. The problem is that GetWindowText() and GetDlgItemText() attempt to optimize by directly examining the window caption of the target window. An edit control stores an empty string in its caption, not the edit control text, so this fails. You can avoid this bug by sending a WM_GETTEXT explicitly: HWND OtherEdit; char Buffer[256]; SendMessage(OtherEdit, WM_GETTEXT, sizeof(Buffer), (LPARAM)Buffer); ------------------------------------------------------------ WDJ SDK Annotation #94 TYPE: Win32 TOPIC: LVM_GETCOLUMNWIDTH KEYWORD: LVM_GETCOLUMNWIDTH AND Parameters The documentation claims that this message returns the column width if successful, "or zero otherwise". In fact, if you specify an invalid column number, this function returns garbage. Therefore, you cannot use this message to count the number of columns in a listview control. The associated message, LVM_GETCOLUMN, does correctly return zero when passed an invalid column number, so you can use LVM_GETCOLUMN to determine the number of columns in a listview control. ------------------------------------------------------------ WDJ SDK Annotation #95 TYPE: Win3.1 TOPIC: SystemParametersInfo (3.1) KEYWORD: SystemParametersInfo The documentation says that the screen saver time out (SPI_GETSCREENSAVETIMEOUT) is specified in milliseconds. In fact, the value returned is in seconds. Submitted by: Carlton Guc ------------------------------------------------------------ WDJ SDK Annotation #96 TYPE: Win32 TOPIC: LVN_ENDLABELEDIT KEYWORD: LVN_ENDLABELEDIT AND Parameters The documentation claims that there is no return value for this message. In fact, you should return FALSE if you want to reject the user's editing, or TRUE if you want to allow the changes to the listview item. If you are handling this notification inside a dialog procedure, remember that you must return a non-zero result AND store the message result (either TRUE or FALSE) in DWL_MSGRESULT, using either SetWindowLong() or the SetDlgMsgResult() macro (defined in windowsx.h). Submitted by: Poul A. Costinsky ------------------------------------------------------------ WDJ SDK Annotation #98 TYPE: Win32 TOPIC: RegSetValueEx KEYWORD: RegSetValueEx AND "See also" For string-based data types, such as REG_SZ, this function behaves differently under Win95 and NT. Under Win95, if you pass a value of "abcd" and a length of 4, the function will actually append a null byte, and if you retrieve the value later, you will find it has a length of 5. Under NT, this function stores exactly what you tell it to store. Submitted by Paula Tomlinson. ------------------------------------------------------------ WDJ SDK Annotation #97 TYPE: Win32 TOPIC: LVN_ITEMCHANGING KEYWORD: LVN_ITEMCHANGING AND Parameters The documentation incorrectly claims that you have to return TRUE to allow the change, or FALSE to prevent it. In fact, you have to return FALSE to allow the change or TRUE to prevent it. Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #99 TYPE: Win32 TOPIC: LV_DISPINFO KEYWORD: LV_DISPINFO AND Parameters The documentation says you can set the LVIF_DI_SETITEM flag in the mask member to have Windows store the string and not ask for it again. It fails to point out that this only works for subitem 0 (the first column), so you would still have to handle requests for the text of the other columns. Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #100 TYPE: Win32 TOPIC: LVM_EDITLABEL KEYWORD: LVM_EDITLABEL AND Parameters Note that the listview control implements in-place editing by creating an edit control on the fly. Unfortunately, it assigns that edit control a child ID of IDOK, which means that the parent of the listview control will receive edit control notifications that it might not expect. For example, if your listview control is in a dialog box, and your dialog procedure contains code like this: // inside WM_COMMAND handler... if(ControlId == IDOK) EndDialog(Dialog, TRUE); Then it may terminate the dialog when the user starts editing a listview item (since the transient edit control will send notifications like EN_CHANGE, with a control ID of IDOK). The above code should read: // inside WM_COMMAND handler... if(ControlId == IDOK && NotifyCode == BN_CLICKED) EndDialog(Dialog, TRUE); to be safe. ------------------------------------------------------------ WDJ SDK Annotation #101 TYPE: Win3.1 TOPIC: GetWindowTextLength (2.x) KEYWORD: GetWindowTextLength Due to a bug in Windows 3.1, both GetWindowTextLength() and WM_GETTEXTLENGTH return -1 if you use it on a combobox of style CBS_DROPDOWNLIST. Submitted by Mircea Neacsu ------------------------------------------------------------ WDJ SDK Annotation #102 TYPE: Win32 TOPIC: LB_ADDSTRING KEYWORD: LB_ADDSTRING AND Parameters If you are planning to add a large number (more than 100) of strings by calling LB_ADDSTRING, LB_INSERTSTRING, LB_DIR, or LB_ADDFILE, consider first sending the new Win32 message LB_INITSTORAGE to give the listbox a chance to preallocate memory, thus speeding up the process. Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #102 TYPE: Win32 TOPIC: LB_ADDSTRING KEYWORD: LB_ADDSTRING AND Parameters If you are planning to add a large number (more than 100) of strings by calling LB_ADDSTRING, LB_INSERTSTRING, LB_DIR, or LB_ADDFILE, consider first sending the new Win32 message LB_INITSTORAGE to give the listbox a chance to preallocate memory, thus speeding up the process. Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #103 TYPE: Win32 TOPIC: LoadString KEYWORD: LoadString AND "See also" Even though resource strings are stored as Unicode for both Win95 and NT programs, Win95 does not provide an implementation of the Unicode version of LoadString() (LoadStringW()). This usually trips up NT programmers who want to create a single .exe for both operating systems and yet still use Unicode under NT. In that case, you must at runtime detect that you are running under Win95 and, in that case, explicitly call LoadStringA(). Otherwise, if you have compiled with _UNICODE defined, LoadString() will expand into LoadStringW(), which is just a stub that fails under Win95. Submitted by Paula Tomlinson. ------------------------------------------------------------ WDJ SDK Annotation #104 TYPE: Win32 TOPIC: LVN_DELETEITEM KEYWORD: LVN_DELETEITEM AND Parameters The documentation implies that this notification arrives after the item is deleted. In fact, it arrives before the item is deleted from the listview control. Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #105 TYPE: Win32 TOPIC: IsWindow KEYWORD: IsWindow AND Parameters The documentation claims this function returns TRUE if the given handle is a valid window handle. That was true under Windows 3.x and is true under Windows NT, but Windows 95 returns a large non-zero value that is not equal to 1 (nor is it equal to the window handle). In an un-indexed help topic in the initial Win95 SDK, Microsoft reveals that Win95 functions with BOOL return types are only guaranteed to return non-zero when the documentation claims they return TRUE. Windows NT appears to behave as documented. Submitted by: David Lowndes ------------------------------------------------------------ WDJ SDK Annotation #105 TYPE: Win32 TOPIC: GetClientRect KEYWORD: GetClientRect AND "See also" The documentation claims this function returns TRUE if the given handle is a valid window handle. That was true under Windows 3.x and is true under Windows NT, but Windows 95 returns a large non-zero value that is not equal to 1 (nor is it equal to the window handle). In an un-indexed help topic in the initial Win95 SDK, Microsoft reveals that Win95 functions with BOOL return types are only guaranteed to return non-zero when the documentation claims they return TRUE. Windows NT appears to behave as documented. Submitted by: Ron Scott ------------------------------------------------------------ WDJ SDK Annotation #106 TYPE: Win32 TOPIC: WM_GETDLGCODE KEYWORD: WM_GETDLGCODE AND Parameters The documentation WM_GETDLGCODE states that this message has no parameters. But the truth is that if the user presses a key, lParam will contain a pointer to a MSG structure containing information about the event that made Windows send the WM_GETDLGCODE message. This is indirectly documented in windowsx.h, where the HANDLE_WM_GETDLGCODE() macro passes two arguments to the handler: the window handle and (MSG FAR*)(lParam). Submitted by: Patrick Tennberg Reference: Microsoft Knowledge Base article Q83302 ------------------------------------------------------------ WDJ SDK Annotation #107 TYPE: Win32 TOPIC: DBT_DEVICEQUERYREMOVE KEYWORD: DBT_DEVICEQUERYREMOVE AND Parameters The documentation incorrectly says that you should return FALSE if you want to veto the device removal. In fact, you have to return BROADCAST_QUERY_DENY; returning FALSE will allow the device removal to proceed. Submitted by Paula Tomlinson. ------------------------------------------------------------ WDJ SDK Annotation #108 TYPE: Win3.1 TOPIC: LZOpenFile KEYWORD: LZOpenFile However, if lpszFile contains only a filename and extension (i.e. no path is specified), then LZOpenFile() use the same searching logic as OpenFile(). If no file with the matching name (and extension) is found, then it searches for the compressed filename. The compressed file has a _ as the last character (e.g., the compressed version of "readme.txt" is "readme.tx_"). Therefore if you use LZOpenFile() to open a file called "readme.txt", it searches as follows: 1) it looks for "readme.txt" using the same search algorithm as OpenFile(). 2) if the file was not found, it uses the same algorithm to search for "readme.tx_". That means LZOpenFile() might open a "readme.tx_" file from a directory other than the one you expected. Even if you specify the complete filename (such as "c:\demo\readme.txt"), LZOpenFile() first searches for the specified file, and if the specified filename is not found, it searches for the compressed file in the same directory ("c:\demo\readme.tx_"). Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #109 TYPE: Win32 TOPIC: SetCapture KEYWORD: SetCapture AND Parameters This help topic does not document the fact that when the mouse is captured, menu hotkeys (for example, Alt+F for accessing the File menu) and other keyboard accelerators do not work (ex: Alt+F4). This is because DefWindowProc() handles the WM_SYSCHAR and WM_SYSCOMMAND messages differently if the mouse has been captured (by any window). DefWindowProc() processes the WM_SYSCHAR message to check if the pressed key is a menu hotkey (Alt+F for example). If it is, then it causes the menu to drop down. However, if the mouse is captured, it does not do the above action. Whenever DefWindowProc() sees a WM_SYSCOMMAND and finds that the mouse is captured, then it does nothing. Submitted by V. Ramachandran. ------------------------------------------------------------ WDJ SDK Annotation #110 TYPE: Win32 TOPIC: RegCreateKey KEYWORD: RegCreateKey AND "See also" The documentation for RegCreateKey(), RegCreateKeyEx(), RegOpenKey(), and RegOpenKeyEx() all list several predefined handles you can use, such as HKEY_CURRENT_USER. However, they fail to document the predefined key HKEY_CURRENT_CONFIG, which has a structure similar to the registry tree under HKEY_LOCAL_MACHINE, but is for storing information specific to the current hardware profile. Submitted by Paula Tomlinson. ------------------------------------------------------------ WDJ SDK Annotation #111 TYPE: Win32 TOPIC: CreateFile KEYWORD: CreateFile AND "See also" The documentation claims CreateFile() returns a handle that can be used to access the object. However, under both NT and Win95, CreateFile() will appear to succeed and return a valid handle if you attempt to open a file with GENERIC_WRITE permissions on a read-only medium (e.g., protected floppy or CD-ROM). If you then try to perform a write with the returned handle, that will fail. Submitted by: David Lowndes ------------------------------------------------------------ WDJ SDK Annotation #112 TYPE: Win32 TOPIC: GetWindowText KEYWORD: GetWindowText AND "See Also" The third argument is the "maximum numbers of characters to copy". It may not be clear that this number must include the NULL byte so, for example, it never makes sense to set this argument to 1 since all you could get back is a NULL byte. If you want to use GetWindowText() to retrieve a single character (e.g., from an edit control), you would have to specify a length of 2 -- one for the character and one for the terminating NULL byte. Submitted by: Tony Yuricich ------------------------------------------------------------ WDJ SDK Annotation #113 TYPE: Win32 TOPIC: keybd_event KEYWORD: keybd_event AND "See also" You can use this function to toggle keys such as the Caps Lock, Scroll Lock, and Num Lock. Unfortunately, though toggling these three keys works correctly under NT, you cannot use this function to toggle the Num Lock key under Win95. ------------------------------------------------------------ WDJ SDK Annotation #114 TYPE: Win3.1 TOPIC: WM_CTLCOLOR (2.x) KEYWORD: WM_CTLCOLOR The documentation incorrectly states that "the return value from this message has no effect on a button with the BS_PUSHBUTTON or BS_DEFPUSHBUTTON style." In fact, returning a brush handle in response to this message appears to determine the color of the pushbutton window background, which is visible as the tiny areas in the corners of the pushbutton window. Note that under Windows 95, pushbuttons fill the entire client area of their windows, thus hiding the window background from view. Submitted by: Forest Wilkinson ------------------------------------------------------------ WDJ SDK Annotation #115 TYPE: Win32 TOPIC: GlobalDeleteAtom KEYWORD: GlobalDeleteAtom AND "See also" The documentation for GlobalDeleteAtom states: "The only way to ensure that an atom has been deleted from the atom table is to call this function repeatedly until it fails." Unfortunately, a 32-bit program running under Win NT 3.51 never fails, making for an infinite loop (Windows 95 functions as documented). Microsoft says this may be fixed in the next release (NT 4.0). Submitted by Ken Brown ------------------------------------------------------------ WDJ SDK Annotation #116 TYPE: Win32 TOPIC: LB_ADDSTRING KEYWORD: LB_ADDSTRING AND "See also" If your listbox has the WS_HSCROLL style, and if you are adding a string wider than the listbox, you may have to send a LB_SETHORIZONTALEXTENT message to make the horizontal scrollbar appear. The listbox does not automatically check each newly added string and add the horizontal scrollbar when a too-wide string is added or inserted. ------------------------------------------------------------ WDJ SDK Annotation #117 TYPE: Win32 TOPIC: LB_DIR KEYWORD: LB_DIR AND "See also" If you pass a long filename to LB_DIR, it works under NT 3.51, but fails under Win95 because it relies on the 16-bit listbox which was not changed to handle long filenames. You can avoid the error by first translating the filename to a short filename by calling GetShortPathName(). The LB_DIR will not fail, but it will still display only the short versions of any long filenames in the subdirectory. Reference: Microsoft Knowledge Base article Q131286 ------------------------------------------------------------ WDJ SDK Annotation #118 TYPE: Win32 TOPIC: ReadFile KEYWORD: ReadFile AND "See also" The documentation correctly points out that Win95 does not support OVERLAPPED I/O. However, it incorrectly states that you must pass a pointer to a structure of type OVERLAPPED if the file was created (or opened) with the flag FILE_FLAG_OVERLAPPED. In fact, if you pass a pointer (rather than NULL) under Windows 95, ReadFile() returns FALSE, and GetLastError() returns ERROR_INVALID_PARAMETER, whether or not the file was created/opened with the FILE_FLAG_OVERLAPPED flag. That means that if you want to code a call to ReadFile() that works correctly for both synchronous and asynchronous I/O, you must detect at runtime that you're running under Win95 and treat that case differently. ----------------------------------------------------------------------- WDJ SDK Annotation #119 TYPE: Win32 TOPIC: WinMain KEYWORD: WinMain AND "See also" Note that the string passed in lpCmdLine is not the same as the string returned by GetCommandLine(). The string in lpCmdLine contains the command line arguments only, but GetCommandLine() returns the program name followed by the arguments. If you specify the following command: 'winword abc.doc', and winword exists in d:\msoffice\winword.exe, lpCmdLine will contain 'abc.doc', while GetCommandLine will return '"d:\msoffice\winword.exe" abc.doc' Note the double quotes around the exe name. Submitted by Phil Rodgers. ---------------------------------------------------------------------- WDJ SDK Annotation #119 TYPE: Win32 TOPIC: GetCommandLine KEYWORD: GetCommandLine AND "See also" Note that the string passed in the lpCmdLine parameter to WinMain is not the same as the string returned by GetCommandLine(). GetCommandLine() returns the complete program name enclosed in double quotes, followed by the arguments; whereas the lpCmdLine parameter of WinMain contains the command line arguments only. If you specify the following command: 'winword abc.doc', and winword exists in d:\msoffice\winword.exe, lpCmdLine will contain 'abc.doc', while GetCommandLine will return '"d:\msoffice\winword.exe" abc.doc' Note the double quotes around the exe name. Submitted by Phil Rodgers. ---------------------------------------------------------------------- WDJ SDK Annotation #120 TYPE: Win32 TOPIC: ShellAbout KEYWORD: ShellAbout AND "See also" The documentation says that in Windows 95 ShellAbout will prepend "Microsoft Windows" to the title of the application. This is not true. Actually, this function prepends only "Microsoft", so if your app is called "MyApp", the title will read "Microsoft MyApp". Submitted by Luis A. Ramos. ---------------------------------------------------------------------- WDJ MFC Annotation #121 TYPE: MFC 4 TOPIC: CPropertySheet::SetTitle KEYWORD: SetTitle AND CPropertySheet The parameters to this function are interchanged: it should actually read: void SetTitle (LPCTSTR lpszText, UINT nStyle); Submitted by Margaret M. O'Connell. ---------------------------------------------------------------------- WDJ MFC Annotation #122 TYPE: MFC 1.5x TOPIC: CCmdTarget::GetIDispatch KEYWORD: GetIDispatch AND CCmdTarget The MFC 1.5x documentation incorrectly states that this function takes no parameters. It should read: LPDISPATCH GetIDispatch (BOOL bAddRef) bAddRef - specifies whether to increment the reference count for the object. This has been corrected in the MFC 4.0 documentation. Submitted by V.Ramachandran. ---------------------------------------------------------------------- WDJ SDK Annotation #123 TYPE: Win16 TOPIC: LB_SELECTSTRING KEYWORD: LB_SELECTSTRING win31wh.hlp fails to mention that this message works only with single select listboxes. For multi-select listboxes it returns LB_ERR. Submitted by Dino Esposito. ---------------------------------------------------------------------- WDJ SDK Annotation #124 TYPE: Win32 TOPIC: AdjustWindowRect KEYWORD: AdjustWindowRect AND "See also" The documentation for AdjustWindowRect() and AdjustWindowRectEx() has a number of problems. 1. Though the documentation says that window titles and borders are not taken into account, the function does account for these styles. 2. Add 1 to the rect.bottom returned by this function. There is a one-off error in the y direction. 3. The meaning of the rect parameter is not well explained. The input to AdjustWindowRectEx is the window coordinates of the top-left and bottom-right corners of the desired client area. AdjustWindowRectEx inflates the specified rectangle to include the caption, border and other non-client objects specified by the style parameter. Reference: MSDN Dr.GUI #10 - Calculated Client Window Size. Submitted by Etay Szekely. ---------------------------------------------------------------------- WDJ SDK Annotation #124 TYPE: Win32 TOPIC: AdjustWindowRectEx KEYWORD: AdjustWindowRectEx AND "See also" The documentation for AdjustWindowRect() and AdjustWindowRectEx() has a number of problems. 1. Though the documentation says that window titles and borders are not taken into account, they function does account for these styles. 2. Add 1 to the rect.bottom returned by this function. There is a one-off error in the y direction. 3. The meaning of the rect parameter is not well explained. The input to AdjustWindowRectEx is the window coordinates of the top-left and bottom-right corners of the desired client area. AdjustWindowRectEx inflates the specified rectangle to include the caption, border and other non-client objects specified by the style parameter. Reference: MSDN Dr.GUI #10 - Calculated Client Window Size. Submitted by Etay Szekely. ---------------------------------------------------------------------- WDJ MFC Annotation #125 TYPE: MFC 3.1 and 3.2 TOPIC: CImageList::DeleteObject KEYWORD: DeleteObject AND "See also" NOT HRESULT VC++ 2.1 and 2.2 help files incorrectly documented this function to delete image lists, while it actually never existed. The correct function to delete an image list is CImageList::DeleteImageList(); it returns non-zero if successful and zero if it fails. This error is corrected in the VC++ 4.0 documentation. Submitted by Barry Tannenbaum. ---------------------------------------------------------------------- WDJ SDK Annotation #126 TYPE: Win32 TOPIC: AVIFileOpen KEYWORD: AVIFileOpen AND "See also" The documentation specifies that using the OF_CREATE flag will cause an existing file to be truncated to zero length. In reality, it has no effect: the file length remains the same, and the old data is intact once the file is closed. Submitted by Tim Lesher. ---------------------------------------------------------------------- WDJ SDK Annotation #127 TYPE: Win16 TOPIC: CBTProc KEYWORD: CBTProc The CBTProc documentation fails to mention that you can return 0 to allow the operation and 1 to prevent it for HCBT_SETFOCUS notification also. The documentation has been corrected in the Win32 help file. Submitted by V.Ramachandran. ---------------------------------------------------------------------- WDJ SDK Annotation #128 TYPE: Win32 TOPIC: GetDialogBaseUnits KEYWORD: GetDialogBaseUnits AND "See also" GetDialogBaseUnits() does not return correct dialog base units if the dialog is not using the system font. Use the following function instead: DWORD WDJ_GetDialogBaseUnits (HWND Dialog) { int BaseX, BaseY; RECT R; SetRect (&R, 0, 0, 4, 8); MapDialogRect (Dialog, &R); BaseX = R.right; BaseY = R.bottom; return (DWORD)MAKELONG (BaseX, BaseY); } Reference: p54, January 1996 Windows Developer's Journal. ---------------------------------------------------------------------- WDJ SDK Annotation #129 TYPE: Win32 TOPIC: GetShortPathName KEYWORD: GetShortPathName AND "See also" The documentation fails to mention that this function returns an error if the specified long path (first parameter) is invalid. Submitted by Ken Brown. ---------------------------------------------------------------------- WDJ MFC Annotation #130 TYPE: MFC TOPIC: CCheckListBox::Create KEYWORD: CCheckListBox::Create AND "See also" The documentation mentions that the dwStyle parameter could be any of the specified listbox styles. However, the dwStyle parameter should NOT be LBS_MULTICOLUMN or LBS_USETABSTOPS. Morever, you need to specify LBS_OWNERDRAWFIXED and LBS_HASSTRINGS. You can specify LBS_OWNERDRAWVARIABLE instead of LBS_OWNERDRAWFIXED, but then you need to override CCheckListBox::DrawItem, otherwise the debug version will ASSERT. Submitted by Sudhir Menon. ---------------------------------------------------------------------- WDJ SDK Annotation #131 TYPE: Win32 TOPIC: TBBUTTON KEYWORD: TBBUTTON AND "See also" The structure is actually defined as follows in commctrl.h. Under Win32 2 extra padding bytes are added. typedef struct _TBBUTTON { int iBitmap; int idCommand; BYTE fsState; BYTE fsStyle; #ifdef _WIN32 BYTE bReserved[2]; #endif DWORD dwData; int iString; } TBBUTTON, NEAR* PTBBUTTON, FAR* LPTBBUTTON; typedef const TBBUTTON FAR* LPCTBBUTTON; Submitted by Eric Heimburg. ---------------------------------------------------------------------- WDJ SDK Annotation #132 TYPE: Win32 TOPIC: EM_POSFROMCHAR KEYWORD: EM_POSFROMCHAR AND "See also" The documentation for the wParam, lParam and return value is completely wrong. It should be as follows: wParam - address of point structure to retrieve the coordinates lParam - zero based index of character Return value - not used Reference: PSS Q137805 Submitted by Fred Heidrich. ---------------------------------------------------------------------- WDJ SDK Annotation #133 TYPE: Win32 TOPIC: EM_CHARFROMPOS KEYWORD: EM_CHARFROMPOS AND "See also" The documentation for the lParam and return value is wrong. It should be as follows: wParam - 0 (not used) lParam - Specifies a pointer to a POINT structure that contains the coordinates for the character position wanted. Return value - specifies the character index Reference: PSS Q137805 Submitted by Fred Heidrich. ---------------------------------------------------------------------- WDJ MFC Annotation #134 TYPE: MFC 4.0 TOPIC: CEdit::PosFromChar KEYWORD: CEdit::PosFromChar AND "See also" The MFC wrapper internally calls the EM_POSFROMCHAR function passing the documented values. Unfortunately, there is a documentation error in EM_POSFROMCHAR, and therefore PosFromChar will not work correctly. Read SDK Annotation #132 or PSS Q137805 for more details. This error has been corrected in MFC 4.1 ---------------------------------------------------------------------- WDJ MFC Annotation #135 TYPE: MFC 4.0 TOPIC: CEdit::CharFromPos KEYWORD: CEdit::CharFromPos AND "See also" The MFC wrapper internally calls the EM_CHARFROMPOS function passing the documented values. Unfortunately, there is a documentation error in EM_CHARFROMPOS, and therefore CharFromPos will not work correctly. Read SDK Annotation #133 or PSS Q137805 for more details. This error has been corrected in MFC 4.1 ---------------------------------------------------------------------- WDJ SDK Annotation #136 TYPE: Win32 TOPIC: BM_SETIMAGE KEYWORD: BM_SETIMAGE AND "See also" The documentation for wParam is incorrect. wParam actually specifies the type of handle passed in lParam. It must be set either IMAGE_BITMAP or IMAGE_ICON. Also, this message will work only on buttons created with the BS_BITMAP or BS_ICON style. Reference: PSS Q125673 Submitted by Paula Tomlinson. ---------------------------------------------------------------------- WDJ SDK Annotation #137 TYPE: Win16 TOPIC: GetOpenFileName KEYWORD: GetOpenFileName In 16-bit Windows, if you call GetOpenFileName with a string buffer of 10 and set nMaxFile to 10, and the user selected a file whose full path name is 10 characters long, Windows appends a NULL character at the 11th byte and returns the filename. This behaviour remains in Win95. Therefore, if you set nMaxFile to n, specify a buffer of length n+1 before calling this function. Reference: March TechNet CD Q137194 Submitted by: V.Ramachandran. ---------------------------------------------------------------------- WDJ SDK Annotation #137 TYPE: Win32 TOPIC: GetOpenFileName KEYWORD: GetOpenFileName AND "See also" In 16-bit Windows, if you call GetOpenFileName with a string buffer of 10 and set nMaxFile to 10, and the user selected a file whose full path name is 10 characters long, Windows appends a NULL character at the 11th byte and returns the filename. This behaviour remains in Win95. Therefore, if you set nMaxFile to n, specify a buffer of length n+1 before calling this function. Reference: March TechNet CD Q137194 Submitted by: V.Ramachandran. ---------------------------------------------------------------------- WDJ SDK Annotation #138 TYPE: Win32 TOPIC: LB_DIR KEYWORD: LB_DIR AND "See also" Sending a LB_DIR message to a listbox that specifies a long filename in the lParam returns LB_ERR in Windows 95, but works fine under Windows NT 3.51 In order to work around this bug, call GetShortPathName on the long filename and pass the short filename to LB_DIR. Reference: March TechNet CD Q131286 Submitted by: V.Ramachandran. ---------------------------------------------------------------------- WDJ SDK Annotation #139 TYPE: Win16 TOPIC: EM_SETSEL KEYWORD: EM_SETSEL The SDK documentation mentions that if wParam is 0, the caret is scrolled into view, and if wParam is 1 it is not scrolled into view. However, this parameter is not used for single line edit controls. Also, the order of the start and end positions specified in the lParam is not respected by single line edit controls. Both wParam and lParam work as documented for multiline edit controls. Reference: MSDN PSS Q102641, Q64758. Submitted by: V.Ramachandran. ---------------------------------------------------------------------- WDJ SDK Annotation #140 TYPE: Win32 TOPIC: STGM KEYWORD: STGM AND "See also" The documentation fails to mention that for all storage and stream creation functions, you HAVE to specify a share mode flag. For example, a call to StgCreateDocfile with STGM_CREATE | STGM_READWRITE will fail; while passing STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE will work correctly. Submitted by: V.Ramachandran. ------------------------------------------------------------------------- WDJ SDK Annotation #141 TYPE: Win32 TOPIC: WM_SETICON KEYWORD: WM_SETICON AND "See also" Contrary to what the documentation says, Windows uses the small icon in the window caption (including the caption of the minimized window). Windows uses the large icon when you press Alt+Tab to switch to another application. ---------------------------------------------------------------------- WDJ SDK Annotation #142 TYPE: Win16 TOPIC: JournalRecordProc KEYWORD: JournalRecordProc lParam is incorrectly documented -- it actually points to a EVENTMSG structure. ---------------------------------------------------------------------- WDJ SDK Annotation #143 TYPE: Win32 TOPIC: GetDeviceCaps KEYWORD: GetDeviceCaps AND "See also" The documentation fails to mention 2 additional values for the index parameter: SCALINGFACTORX - Scaling factor for the x-axis of a printer SCALINGFACTORY - Scaling factor for the y-axis of a printer Also, GetDeviceCaps provides the following 6 indices in place of printer Escapes: Index for GetDeviceCaps Printer Escape Replaced -------------------------------------------------------- PHYSICALWIDTH GETPHYSPAGESIZE PHYSICALHEIGHT GETPHYSPAGESIZE PHYSICALOFFSETX GETPRINTINGOFFSET PHYSICALOFFSETY GETPRINTINGOFFSET SCALINGFACTORX GETSCALINGFACTOR SCALINGFACTORY GETSCALINGFACTOR Reference: MSDN Knowledge Base PSS Q125692 Submitted by Sudhir Menon. ---------------------------------------------------------------------- WDJ SDK Annotation #144 TYPE: Win32 TOPIC: MessageBoxIndirect KEYWORD: MessageBoxIndirect AND "See also" The documentation incorrectly mentions that this function returns a BOOL value, and gives no description of it. In fact, the return value is an int, and it has the same meaning as the return value for MessageBox. Submitted by Phil Rodgers. ---------------------------------------------------------------------- WDJ SDK Annotation #145 TYPE: Win32 TOPIC: LVM_EDITLABEL KEYWORD: LVM_EDITLABEL AND "See also" Note that the listview control allows label editing by creating a child edit control with an ID of IDOK. That edit control will send notifications to the parent of the listview control (e.g., your dialog box), so if you have an "OK" button with the same ID, do not write code like this: if(Command == IDOK) EndDialog(Dialog, TRUE); since the first time the user edits a label, the dialog will immediately disappear as though the user pressed the "OK" button. Instead, use code like this: if(Command == IDOK && Code == BN_CLICKED) EndDialog(Dialog, TRUE); ---------------------------------------------------------------------- WDJ SDK Annotation #146 TYPE: Win32 TOPIC: TVN_ENDLABELEDIT KEYWORD: TVN_ENDLABELEDIT AND "See also" When editing labels in a TreeView control which is part of a dialog, this notification is sent only if the user ends label editing using the mouse (by clicking outside the edit control). Using the Enter and Return keys do not work because the edit control does not handle the WM_GETDLGCODE correctly, and therefore IsDialogMessage processes these keys. In order to make the TreeView control accept the changes when the user presses Enter, and reject the changes on Escape, do the following: 1. In the TVN_BEGINLABELEDIT notification, get the edit contol handle using TVM_GETEDITCONTROL. Subclass the edit control. 2. In the subclassed procedure, handle the WM_GETDLGCODE message and return WM_WANTALLKEYS. 3. Handle the WM_CHAR message. When wParam is VK_ESCAPE, send a TVM_ENDLABELEDITNOW message to the edit control with fCancel = TRUE. When wParam is VK_RETURN, send TVM_ENDLABELEDITNOW with fCancel = FALSE. All other WM_CHAR messages should be passed to the default edit control procedure. 4. In the TVN_ENDLABELEDIT notification, remove the subclassing. Reference: MSDN article PSS Q130691 ---------------------------------------------------------------------- WDJ SDK Annotation #147 TYPE: Win16 TOPIC: GetWindowTextLength KEYWORD: GetWindowTextLength In Windows 3.1, calling GetWindowTextLength on a drop list combo box (CBS_DROPDOWNLIST) incorrectly returns -1. However in Win32 (for both 16 and 32 bit applications), GetWindowTextLength returns the length of the string in the static portion of the combo box. Submitted by Sinisa Djurekovic. ---------------------------------------------------------------------- WDJ SDK Annotation #147 TYPE: Win32 TOPIC: GetWindowTextLength KEYWORD: GetWindowTextLength AND "See also" In Windows 3.1, calling GetWindowTextLength on a drop list combo box (CBS_DROPDOWNLIST) incorrectly returns -1. However in Win32 (for both 16 and 32 bit applications), GetWindowTextLength returns the length of the string in the static portion of the combo box. Submitted by Sinisa Djurekovic. ---------------------------------------------------------------------- WDJ MFC Annotation #147 TYPE: MFC 1.x, 2.x TOPIC: CWnd::GetWindowText KEYWORD: CWnd::GetWindowText AND CString AND Parameters If you call GetWindowText (CString&) on a CWnd object which specifies a Combo box with the CBS_DROPDOWNLIST style, you will get an assertion in strcore.cpp. This is because GetWindowText internally calls GetWindowTextLength. Calling GetWindowTextLength on a drop list combo box (CBS_DROPDOWNLIST) incorrectly returns -1, and MFC asserts because -1 is an invalid length for a CString object. Submitted by Sinisa Djurekovic. ---------------------------------------------------------------------- WDJ SDK Annotation #148 TYPE: Win32 TOPIC: FindFirstFile KEYWORD: FindFirstFile AND "See also" In Windows 95, the FindFirstFile() function interprets a wildcard (?) as "any character" instead of "zero or one character," as you would expect. For example, if the files, TEMP.TXT and TEMPTEMP.TXT, are in the same directory, using FindFirstFile ("TEM?????.???", &findData) finds the TEMPTEMP.TXT file, but not the TEMP.TXT file: This function works correctly under Windows NT. Reference: MSDN PSS ID No. Q130860 ---------------------------------------------------------------------- WDJ SDK Annotation #149 TYPE: Win32 TOPIC: CreateDirectoryEx KEYWORD: CreateDirectoryEx AND "See also" When you specify a template directory string with two back slashes (\\) at the end of the string, CreateDirectoryEx returns FALSE indicating error, even though the API successfully created the new directory. GetLastError returns ERROR_INVALID_PARAMETER (87L). Under Windows 95, passing "c:\\" as the template directory (first parameter) to CreateDirectoryEx will return FALSE, even if it successfully created the new directory. The function works correctly under Windows NT. Reference: MSDN PSS ID No. Q140455 ---------------------------------------------------------------------- WDJ MFC Annotation #150 TYPE: MFC TOPIC: CStatusBar::SetPaneText KEYWORD: CStatusBar::SetPaneText AND "See also" Just calling CStatusBar::SetPaneText will not display the text in the status bar pane. You need to add a UI update handler for the pane for the text to appear correctly. In order to set the text for pane index 4, and id ID_PANE_FOUR do: SetPaneText (4, "Some text", TRUE); and a UI handler in the message map as follows: ON_UPDATE_COMMAND_UI (ID_PANE_FOUR, OnUpdatePane) and in the appropriate .cpp file add: void CMyClass::OnUpdatePane (CCmdUI *pCmdUI) { pCmdUI->Enable (); } Reference: MSDN PSS No. Q109039 ---------------------------------------------------------------------- WDJ MFC Annotation #151 TYPE: MFC 2.x TOPIC: CRecordSet::Open KEYWORD: CRecordSet::Open The documentation says that, for the param dwOptions, an enum "CRecordset::defaultOptions" can be specified, which will make the recordset updatable. However, this enum is not defined. Use "CRecordset::none" instead. Submitted by Nagendra R. ---------------------------------------------------------------------- WDJ SDK Annotation #152 TYPE: Win16 TOPIC: EnumFontFamProc KEYWORD: EnumFontFamProc The SDK documentation incorrectly mentions that the first parameter lpnlf points to a NEWLOGFONT structure, which is not defined at all. lpnlf actually points to a ENUMLOGFONT structure. Reference: MSDN KB Q87975 Submitted by V.Ramachandran. ---------------------------------------------------------------------- WDJ SDK Annotation #153 TYPE: Win32 TOPIC: WM_CANCELMODE KEYWORD: WM_CANCELMODE AND "See also" The WM_CANCELMODE gets sent to the active window before another dialog or message box is displayed. It is not sent to the focus window as the documentation claims. Therefore if a OK Button in Dialog 1 displays Dialog 2, Dialog 1 gets a WM_CANCELMODE message and not the OK button, which should have been the case as per the documentation. Submitted by V.Ramachandran. ---------------------------------------------------------------------- WDJ SDK Annotation #154 TYPE: Win32 TOPIC: CreateWindow KEYWORD: CreateWindow AND "See also" The documentation does not mention that static controls created with the SS_SIMPLE style do not gray their text when they are disabled unlike static controls without the SS_SIMPLE style. Submitted by Nagendra R. ---------------------------------------------------------------------- WDJ MFC Annotation #155 TYPE: MFC 4.x TOPIC: CFileDialog::GetNextPathName KEYWORD: CFileDialog::GetNextPathName If you select multiple files in a root drive, GetNextPathName returns strings with two back slashes '\' after the drive letter. For example, if you select the autoexec.bat and config.sys files from C:\ using a multiple selection file open dialog, GetNextPathName will return the strings: "c:\\autoexec.bat" and "c:\\config.sys". This problem does not appear when you select only one file. A workaround would be to check the strings returned by GetNextPathName for double back slashes, and remove them yourself. CString sFile = dlg.GetNextPathName (pos); #if _MFC_VER >= 0x0400 if ((sFile [1] == ':') && (sFile [2] == '\\') && (sFile [3] == '\\')) { sFile = sFile.Left (3) + sFile.Right (sFile.GetLength () - 4); } #endif // end of MFC 4.x hack! Submitted by David Lowndes. ---------------------------------------------------------------------- WDJ MFC Annotation #156 TYPE: MFC 4.x TOPIC: CToolTipCtrl::GetToolInfo KEYWORD: CToolTipCtrl::GetToolInfo The documentation incorrectly states that the first parameter should be of type LPTOOLINFO. The function actually accepts a CToolInfo& (reference to a undocumented CToolInfo class). If the call succeeds, the szText parameter of the CToolInfo variable contains the tooltip text. Submitted by Paul Stemper. ---------------------------------------------------------------------- WDJ MFC Annotation #157 TYPE: MFC 4.x TOPIC: CToolTipCtrl::AddTool KEYWORD: CToolTipCtrl::AddTool The documentation fails to mention that you cannot set a tooltip to a static control using the AddTool function. Submitted by Sudhir Menon. ---------------------------------------------------------------------- WDJ MFC Annotation #158 TYPE: MFC 4.x TOPIC: CTreeCtrl::GetNextVisibleItem KEYWORD: CTreeCtrl::GetNextVisibleItem GetNextVisibleItem and GetPrevVisibleItem return the next or previous item, not the next or previous visible item. Submitted by Mark Gorokhov. ---------------------------------------------------------------------- WDJ MFC Annotation #158 TYPE: MFC 4.x TOPIC: CTreeCtrl::GetPrevVisibleItem KEYWORD: CTreeCtrl::GetPrevVisibleItem GetNextVisibleItem and GetPrevVisibleItem return the next or previous item, not the next or previous visible item. Submitted by Mark Gorokhov. ---------------------------------------------------------------------- WDJ MFC Annotation #159 TYPE: MFC 4.x TOPIC: CFontDialog::GetCurrentFont KEYWORD: CFontDialog::GetCurrentFont Though the documentation mentions that you can call this function after calling DoModal, the function incorrectly ASSERTS that its window handle is not NULL. Instead, use the public CFontDialog member variable m_lf. Submitted by Tim Lesher. ---------------------------------------------------------------------- WDJ SDK Annotation #160 TYPE: Win32 TOPIC: WM_MOVE KEYWORD: WM_MOVE AND "See also" The SDK documentation tells you to use the following two lines to calculate the x, and y positions of the window after it has been moved. xPos = (int) LOWORD(lParam); // horizontal position yPos = (int) HIWORD(lParam); // vertical position Though this worked fine in 16-bit Windows, it will fail for negative coordinates in 32-bit Windows. Instead use the following two lines to calculate the coordinates: xPos = (int) (short)LOWORD(lParam); // horizontal position yPos = (int)(short) HIWORD(lParam); // vertical position Submitted by Steven M. Kinney. ---------------------------------------------------------------------- WDJ MFC Annotation #161 TYPE: MFC 4.x TOPIC: CWnd::OnMove KEYWORD: CWnd::OnMove AND Parameters NOT LPRECT MFC supplies LOWORD (lParam) and HIWORD (lParam) of the WM_MOVE message for the x, and y parameters of this function, respectively. Since x and y are defined as integers, you will get very large numbers for negative coordinates. Whenever you want to handle the WM_MOVE message, do the following: 1. Define a message handler: ON_MESSAGE (WM_MOVE, OnMyMove) in the message map. 2. Write the OnMyMove function as follows: LRESULT ClassName::OnMyMove (WPARAM, LPARAM lParam) { x = (int) (short)LOWORD(lParam); // horizontal position y = (int)(short) HIWORD(lParam); // vertical position // Do your processing here ... return Default (); } Submitted by Steven M. Kinney. ---------------------------------------------------------------------- WDJ MFC Annotation #162 TYPE: MFC 2.x TOPIC: CRecordset::Delete KEYWORD: CRecordset::Delete AND Remarks NOT Symptoms This function is incorrectly documented as returning BOOL. It actually returns void. This documentation error has been corrected in MFC 4.x Submitted by Tushar Bhatia. ---------------------------------------------------------------------- WDJ SDK Annotation #163 TYPE: Win32 TOPIC: EM_GETLINE KEYWORD: EM_GETLINE AND "See also" When using this message under Win32s (1.30a and above) with the RichEdit control, the parameters the message takes are: wParam = 0; lParam = &em32s; Where em32s is a structure defined as: struct EM32S { DWORD wParam; DWORD lParam; }; The documented wParam and lParam values are stored in the EM32S structure. Submitted by Larry Widing. ---------------------------------------------------------------------- WDJ SDK Annotation #163 TYPE: Win32 TOPIC: EM_LINEINDEX KEYWORD: EM_LINEINDEX AND "See also" When using this message under Win32s (1.30a and above) with the RichEdit control, the parameters the message takes are: wParam = 0; lParam = &em32s; Where em32s is a structure defined as: struct EM32S { DWORD wParam; DWORD lParam; }; The documented wParam and lParam values are stored in the EM32S structure. Submitted by Larry Widing. ---------------------------------------------------------------------- WDJ SDK Annotation #163 TYPE: Win32 TOPIC: EM_LINELENGTH KEYWORD: EM_LINELENGTH AND "See also" When using this message under Win32s (1.30a and above) with the RichEdit control, the parameters the message takes are: wParam = 0; lParam = &em32s; Where em32s is a structure defined as: struct EM32S { DWORD wParam; DWORD lParam; }; The documented wParam and lParam values are stored in the EM32S structure. Submitted by Larry Widing. ---------------------------------------------------------------------- WDJ SDK Annotation #163 TYPE: Win32 TOPIC: EM_LINESCROLL KEYWORD: EM_LINESCROLL AND "See also" When using this message under Win32s (1.30a and above) with the RichEdit control, the parameters the message takes are: wParam = 0; lParam = &em32s; Where em32s is a structure defined as: struct EM32S { DWORD wParam; DWORD lParam; }; The documented wParam and lParam values are stored in the EM32S structure. Submitted by Larry Widing. ---------------------------------------------------------------------- WDJ SDK Annotation #164 TYPE: Win32 TOPIC: EDITSTREAM KEYWORD: EDITSTREAM AND "See also" The return value from this function is incorrectly documented. The return value from the EditStreamCallback is interpreted as an SCODE - you should return 0 to indicate success or an error code otherwise (both while reading and writing). Reference: MSDN KB article Q136810 Submitted by Donald Munro. ---------------------------------------------------------------------- WDJ SDK Annotation #165 TYPE: Win16 TOPIC: GetNextDlgGroupItem KEYWORD: GetNextDlgGroupItem The Win16 SDK fails to mention that this function ignores controls that are part of the group but are disabled. The documentation has been corrected in Win32. If you use MFC, check out Annotation #166 (DDX_Radio) for this bug's repercussions. ---------------------------------------------------------------------- WDJ MFC Annotation #166 TYPE: MFC TOPIC: DDX_Radio KEYWORD: DDX_Radio DDX_Radio internally uses GetNextDlgGroupItem (see SDK annotation #165) and therefore ignores disabled radio-buttons. This could causes 2 radio buttons in the same group to be selected, if a disabled radio-button is selected. Generally, you are better off not using this function if you are going to enable/disable radio-buttons. Use the Windows SDK functions instead. This applies to MFC 1.x and 2.x. Reference: MSDN KB Q114980 ---------------------------------------------------------------------- WDJ SDK Annotation #167 TYPE: Win32 TOPIC: CreatePen KEYWORD: CreatePen AND "See also" If you specify a width greater than 1 for one of the the following styles - PS_DASH, PS_DOT, PS_DASHDOT, PS_DASHDOTDOT, CreatePen returns a pen with the specified width but with the PS_SOLID style. Submitted by Muthuvale Shanmugam. ---------------------------------------------------------------------- WDJ SDK Annotation #168 TYPE: Win32 TOPIC: WindowFromPoint KEYWORD: WindowFromPoint AND "See also" If given a point over a static text control, WindowFromPoint() returns the handle of the window "under" the static text control. Submitted by Muthuvale Shanmugam. ---------------------------------------------------------------------- WDJ SDK Annotation #169 TYPE: Win32 TOPIC: TranslateMessage KEYWORD: TranslateMessage AND "See also" Though the documentation mentions that it returns TRUE only if it translates message, it actually returns TRUE for all WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN and WM_SYSKEYUP messages. This behaviour is consistent with Windows 3.1 Reference: MSDN KB Q137231 ---------------------------------------------------------------------- WDJ SDK Annotation #170 TYPE: Win32 TOPIC: LoadLibrary KEYWORD: LoadLibrary AND "See also" When writing code for a Win32 DLL, you may be tempted to use declare thread-local variables using Windows-specific keywords like this: __declspec(thread) int ThisThreadStatus; If you do, however, then the resulting DLL can only be loaded implicitly (by linking with an import library). If any program tries to use LoadLibrary() or LoadLibraryEx() to load such a DLL, the call will fail and GetLastError() will report a code of 1114L (DLL initialization failed), even though your DLL's entry point never gets called. The only work around is to simply not use __declspec(thread) in your DLL. Either use the TlsXxxx() functions to allocate thread-local storage, or use your own algorithm for associating data with thread handles. ---------------------------------------------------------------------- WDJ SDK Annotation #171 TYPE: Win32 TOPIC: GetLastError KEYWORD: GetLastError AND "See also" NOT MAPIERROR Some versions of the documentation for GetLastError() state that a listing of the error codes is found in WINNT.H. The list of error codes is actually found in the file WINERROR.H. Submitted by Katy Mulvey. ---------------------------------------------------------------------- WDJ SDK Annotation #172 TYPE: Win32 TOPIC: WaitForMultipleObjects KEYWORD: WaitForMultipleObjects AND "See also" The Windows 95 implementation of this function has a bug (see the reference for a complete explanation). If the same thread claims a mutex more than once, then a second thread calls WaitForMultipleObjects() to wait on that mutex and at least one other object, WaitForMultipleObjects() will incorrectly consider the mutex free when the other objects being waited on become signaled. Reference: p. 44, December 1996 Windows Developer's Journal ---------------------------------------------------------------------- WDJ SDK Annotation #173 TYPE: Win32 TOPIC: TBBUTTON KEYWORD: TBBUTTON AND "See also" The documentation incorrectly claims that for separators (fsStyle equal to TBSTYLE_SEP), the idCommand field must be zero. In fact, you can assign toolbar separators a non-zero ID. In fact, it is important to assign unique IDs to separators if they have non-default widths so the customization features will work properly. Also, it's a common technique to use a "fat" separator as a placeholder for a control (such as a drop-down listbox) that you want to appear on the toolbox. You will need to assign such separators a non-zero ID so that you can locate the correct position for the control. Reference: p. 45, November 1996 Windows Developer's Journal ---------------------------------------------------------------------- WDJ SDK Annotation #174 TYPE: Win32 TOPIC: WM_GETDLGCODE KEYWORD: WM_GETDLGCODE AND "See also" If a 32-bit control returns DLGC_HASSETSEL, Windows 95 will not send a EM_SETSEL message when the control gets the focus. Instead, Windows 95 sends the control a message with the same value as the 16-bit EM_SETSEL (0x401) with 16-bit style parameters. Under Windows NT, your control will get the normal 32-bit EM_SETSEL message. Note: Under Win32, the EM_ messages start at 0xB0 and not at WM_USER (0x400) as they did in Win16. Submitted by Robert Mashlan. ------------------------------------------------------------------------ WDJ SDK Annotation #175 TYPE: Win32 TOPIC: GetRegionData KEYWORD: GetRegionData AND "See also" The documentation for the return value is incorrect. If the function succeeds and dwCount specifies an adequate number of bytes, the return value is always dwCount. If the function fails or if dwCount specifies less than adequate number of bytes, the return value is 0. Submitted by Peter C. Jahans. ---------------------------------------------------------------------- WDJ MFC Annotation #175 TYPE: MFC TOPIC: CRgn::GetRegionData KEYWORD: CRgn::GetRegionData The documentation for the return value is incorrect. If the function succeeds and dwCount specifies an adequate number of bytes, the return value is always dwCount. If the function fails or if dwCount specifies less than adequate number of bytes, the return value is 0 (ERROR). Submitted by Peter C. Jahans. ---------------------------------------------------------------------- WDJ SDK Annotation #176 TYPE: Win32 TOPIC: DrawFocusRect KEYWORD: DrawFocusRect AND "See also" DrawFocusRect() works only in MM_TEXT mode. In other modes, this function does not draw the focus rectangle correctly, but it does NOT return error values! Submitted by Jon-David Wiesman. ---------------------------------------------------------------------- WDJ MFC Annotation #176 TYPE: MFC TOPIC: CDC::DrawFocusRect KEYWORD: CDC::DrawFocusRect CDC::DrawFocusRect() works only in MM_TEXT mode. In other modes, this function does not draw the focus rectangle correctly, but it does NOT return error values! Submitted by Jon-David Wiesman. ---------------------------------------------------------------------- WDJ SDK Annotation #177 TYPE: Win32 TOPIC: FSCTL_DISMOUNT_VOLUME KEYWORD: FSCTL_DISMOUNT_VOLUME AND "See also" The SDK documentation for this function mentions "If the specified volume is locked, the operation fails." This is incorrect. It should actually read "If the specified volume is locked by another process, the operation fails." You must do an FSCTL_LOCK_VOLUME before you can do an FSCTL_DISMOUNT_VOLUME. Also, in the sequence of events for reformatting FAT to NTFS as described in the same article, steps 4 and 5 must be switched - you need to dismount the volume before you unlock the volume. Submitted by Jeffrey S. Goldner. ---------------------------------------------------------------------- WDJ SDK Annotation #178 TYPE: Win32 TOPIC: CreateProcess KEYWORD: CreateProcess AND "See also" If you call CreateProcess() with a dwCreationFlags value which includes DEBUG_PROCESS, the call will fail under Win95 when the debugee is a 16-bit app. Submitted by Marc Cousineau. ---------------------------------------------------------------------- WDJ SDK Annotation #179 TYPE: Win32 TOPIC: WM_ACTIVATE KEYWORD: WM_ACTIVATE AND "See also" The documentation states that the message is sent first to the top-level window being deactivated and then to the window being activated. This is incorrect in Win32. In Win32, activation is asynchronous - the activating application becomes active immediately and the messages to the deactivating application occur later. Reference: MSDN KB Article Q135785 Submitted by V.Ramachandran. ---------------------------------------------------------------------- WDJ MFC Annotation #179 TYPE: MFC TOPIC: CWnd::OnActivate KEYWORD: CWnd::OnActivate The documentation states that first OnActivate() is called for the main window being deactivated, and then for the main window being activated This is incorrect in Win32. In Win32, activation is asynchronous - the activating application becomes immediately and the messages to the deactivating application occur later. Reference: MSDN KB Article Q135785 Submitted by V.Ramachandran. ---------------------------------------------------------------------- WDJ SDK Annotation #180 TYPE: Win32 TOPIC: DRAWITEMSTRUCT KEYWORD: DRAWITEMSTRUCT AND "See also" Listviews always set the itemAction member to ODA_DRAWENTIRE. You need to check the itemState member to check if the focus or selection needs to be updated. Reference: MSDN KB Article Q131788 Submitted by V.Ramachandran. ---------------------------------------------------------------------- WDJ MFC Annotation #181 TYPE: MFC 4.2 TOPIC: CInternetSession::ServiceTypeFromHandle KEYWORD: CInternetSession::ServiceTypeFromHandle The on-line help states CInternetSession::ServiceTypeFromHandle() returns the type of service given the internet handle. Although this function is declared in afxinet.h, it is not defined anywhere. Using this function will cause your program to fail at link time. Submitted by Mario Contestabile. ---------------------------------------------------------------------- WDJ MFC Annotation #182 TYPE: MFC 4.2 TOPIC: CHttpFile::QueryInfo KEYWORD: CHttpFile::QueryInfo The documentation for CHttpFile::QueryInfo() erroneously states some of the possible values for lpdwIndex and dwIndex: HTTP_QUERY_LANGUAGE is actually HTTP_QUERY_CONTENT_LANGUAGE HTTP_QUERY_ALLOWED_METHODS is actually HTTP_QUERY_ALLOW HTTP_QUERY_PUBLIC_METHODS is actually HTTP_QUERY_PUBLIC (All #defines in wininet.h) Submitted by Mario Contestabile. ---------------------------------------------------------------------- WDJ MFC Annotation #183 TYPE: MFC 4.x TOPIC: COleDateTime::GetYear KEYWORD: COleDateTime::GetYear If the status of this COleDateTime object is not valid, the return value is AFX_OLE_DATETIME_ERROR and not AFX_DATETIME_ERROR as documented. This affects the following member functions: GetYear(), GetMonth(), GetDay(), GetHour(), GetMinute(), GetSecond(), GetDayOfWeek(), and GetDayOfYear(). Submitted by Mario Contestabile. ---------------------------------------------------------------------- WDJ SDK Annotation #184 TYPE: Win32 TOPIC: ScrollWindowEx KEYWORD: ScrollWindowEx AND "See also" If the prcClip parameter is NULL, no clipping is performed on the scroll rectangle. Submitted by V. Ramachandran. ---------------------------------------------------------------------- WDJ SDK Annotation #185 TYPE: Win32 TOPIC: WM_CONTEXTMENU KEYWORD: WM_CONTEXTMENU AND "See also" If the user uses the keyboard shortcut (Shift-F10 or the special key on a Microsoft keyboard) to activate the context menu, the xPos and yPos parameters are -1, -1. Submitted by Katy Mulvey. ---------------------------------------------------------------------- WDJ SDK Annotation #186 TYPE: Win32 TOPIC: GetLastError KEYWORD: GetLastError AND "return values" AND parameters NOT S_OK The Win32 Programmer's Reference documentation for GetLastError() incorrectly states that a listing of the error codes is found in WINNT.H. It is actually found in the file WINERROR.H Note: This error has been corrected in the Jan 97 MSDN. Submitted by Katy Mulvey. ---------------------------------------------------------------------- WDJ SDK Annotation #187 TYPE: Win32 TOPIC: CreateWindowEx KEYWORD: CreateWindowEx AND "See also" The documentation does not mention that you can call GetLastError to get extended error information if the function fails. Submitted by V. Ramachandran. ---------------------------------------------------------------------- WDJ SDK Annotation #188 TYPE: Win32 TOPIC: WM_SYSCOMMAND KEYWORD: WM_SYSCOMMAND AND "See also" If the wParam is SC_KEYMENU, the lParam contains the character code of the key used in combination with the Alt key to display the popup menu. For example, pressing Alt+F to display the File popup will cause a WM_SYSCOMMAND with wParam equal to SC_KEYMENU and lParam equal to 'f'. Reference: MSDN Article ID Q92527. Submitted by V. Ramachandran. ---------------------------------------------------------------------- WDJ SDK Annotation #189 TYPE: Win32 TOPIC: CreateWindow KEYWORD: CreateWindow AND "See also" For creating static controls with the SS_ICON style, you need to specify the icon name in the lpWindowName parameter. However, if you have an icon resource identified with a numeric id (instead of a string), you need to specify #xxx (where xxx is the numeric identifier for the ICON resource) in the lpWindowName parameter. You should not use the MAKEINTRESOURCE macro. Submitted by V. Ramachandran. ---------------------------------------------------------------------- WDJ SDK Annotation #189 TYPE: Win32 TOPIC: CreateWindowEx KEYWORD: CreateWindowEx AND "See also" For creating static controls with the SS_ICON style, you need to specify the icon name in the lpWindowName parameter. However, if you have an icon resource identified with a numeric id (instead of a string), you need to specify #xxx (where xxx is the numeric identifier for the ICON resource) in the lpWindowName parameter. You should not use the MAKEINTRESOURCE macro. Submitted by V. Ramachandran. ---------------------------------------------------------------------- WDJ SDK Annotation #190 TYPE: Win32 TOPIC: WM_GETTEXT KEYWORD: WM_GETTEXT AND "See also" In Win32, you cannot send the WM_SETTEXT to a static control with the SS_ICON style to set the icon. Therefore, you should not use the WM_GETTEXT message to retrieve the icon handle of static controls with the SS_ICON style. Use the STM_SETICON and STM_GETICON messages instead. Submitted by V. Ramachandran. ---------------------------------------------------------------------- WDJ SDK Annotation #191 TYPE: Win32 TOPIC: RegisterHotKey KEYWORD: RegisterHotKey AND "See also" If you want to disable task switching under Win32, you can do so by registering hot keys for Ctrl+Esc and Alt+Tab. Reference: MSDN Article ID Q125614 Submitted by V. Ramachandran. ---------------------------------------------------------------------- WDJ SDK Annotation #192 TYPE: Win32 TOPIC: CreateFile KEYWORD: CreateFile AND "See also" Under Windows NT, when CreateFile is used with the dwCreationDistribution parameter equal to CREATE_ALWAYS, the call fails if the file exists and is hidden. GetLastError() returns ERROR_ACCESS_DENIED. However, the call succeeds under Windows 95 creating a new file that is not hidden. Submitted by Abdul Nizar. ---------------------------------------------------------------------- WDJ MFC Annotation #192 TYPE: MFC 4.x TOPIC: CFile::Open KEYWORD: CFile::Open Under Windows NT, if the nOpenFlags parameter is ORed with CFile::modeCreate and not ORed with CFile::modeNoTruncate, CFile::Open fails if the file exists and is hidden. However, the call succeeds under Windows 95 creating a new file that is not hidden. Submitted by Abdul Nizar. ---------------------------------------------------------------------- WDJ MFC Annotation #193 TYPE: MFC TOPIC: CListCtrl::SetColumnWidth KEYWORD: CListCtrl::SetColumnWidth The cx parameter is the new width of the column in listview coordinates, or one of the following LVSCW_AUTOSIZE Automatically sizes the column. LVSCW_AUTOSIZE_USEHEADER Automatically sizes the column to fit the header text Submitted by Ramon de Klein. ----------------------------------------------------------------------